SECTION 11 FUNCTIONS WITH FUNCTIONAL ARGUMENTS As in all LISP 1.5 Systems, arguments can be passed which can then be used as functions. However, since car of a form is never evaluated, apply or apply* must be used to call the function specified by the value of the functional argument. Functions which use functional arguments should use variables with obscure names to avoid possible conflict with variables that are used by the functional argument. For example, all system functions standardly use variable names consisting of the function name concatenated with x or fn, e.g., mapx. Note that by specifying the free variables used in a functional argument as the second argument to function, thereby using the INTERLISP FUNARG feature, the user can be sure of no clash. function[fn;env] is an nlambda function. If env=NIL, the value of function is identical to quote, for example, (MAPC LST (FUNCTION PRINT)) will cause mapc to be called with two arguments the value of lst and PRINT. Similarly, [MAPCAR LST(FUNCTION(LAMBDA(Z) (LIST(CAR Z] will cause mapcar to be called with the value of lst and (LAMBDA (Z) (LIST (CAR Z))). When compiled, function will cause code to be compiled for fn; quote will not. Thus (MAPCAR LST (QUOTE (LAMBDA --))) will cause mapcar to be called with the value of lst and the expression (LAMBDA --). The functional argument will therefore still be interpreted. The corresponding expression using function will cause a dummy function to be created with definition (LAMBDA --), and then compiled. mapcar would then be called with the value of lst and the name of the dummy function. See Section 18. If env is not NIL, it can be a list of variables that are (presumably) used freely by fn. In this case, the value of function is an expression of the form (FUNARG fn pos), where pos is a stack pointer to a frame that contains the 11.1 variable bindings for those variables on env. env can also be a stack pointer itself, in which case the value of function is (FUNARG fn env). Finally, env can be an atom, in which case it is evaluated, and the value interpreted as described above. Funarg is described on page 11.4-5. map[mapx;mapfn1;mapfn2] If mapfn2 is NIL, map applies the function mapfn1 to successive tails of the list mapx. That is, first it computes mapfn1[mapx], and then mapfn1[cdr[mapx]], 1 etc., until mapx is exhausted. If mapfn2 is provided, mapfn2[mapx] is used instead of cdr[mapx] for the next call for mapfn1, e.g., if mapfn2 were cddr, alternate elements of the list would be skipped. The value of map is NIL. map compiles open. mapc[mapx;mapfn1;mapfn2] Identical to map, except that mapfn1[car[mapx]] is computed at each iteration instead of mapfn1[mapx], i.e., mapc works on elements, map on tails. The value of mapc is NIL. mapc compiles open. maplist[mapx;mapfn1;mapfn2] successively computes the same values that map would compute; and returns a list consisting of those values. maplist compiles open. mapcar[mapx;mapfn1;mapfn2] computes the same values that mapc would compute, and returns a list consisting of those values, e.g., mapcar[x;FNTYP] is a list of fntyps for each element on x. mapcar compiles open. mapcon[mapx;mapfn1;mapfn2] Computes the same values as map and maplist but nconcs these values to form a list which it returns. mapcon compiles open. mapconc[mapx;mapfn1;mapfn2] Computes the same values as mapc and mapcar, but nconcs the values to form a list which it returns. mapconc compiles open. ------------------------------------------------------------------------ 1 i.e., becomes a non-list. 11.2 Note that mapcar creates a new list which is a mapping of the old list in that each element of the new list is the result of applying a function to the corresponding element on the original list. mapconc is used when there are a variable number of elements (including none) to be inserted at each iteration, e.g. mapconc[X;(LAMBDA (Y) (AND Y (LIST Y)))] will make a list consisting of x with all NILs removed, mapconc[X;(LAMBDA (Y) (AND (LISTP Y) Y))] will make a linear list consisting of all the lists on x, e.g., if applied to 2 ((A B) C (D E F) (G) H I) will yield (A B D E F G). subset[mapx;mapfn1;mapfn2] applies mapfn1 to elements of mapx and returns a list of those elements for which this application is non-NIL, e.g., subset[(A B 3 C 4);NUMBERP] = (3 4). mapfn2 plays the same role as with map, mapc, et al. subset compiles open. map2c[mapx;mapy;mapfn1;mapfn2] Identical to mapc except mapfn1 is a function of two arguments, and mapfn1[car[mapx];car[mapy]] is computed at 3 each interation. Terminates when either mapx or mapy are exhausted. map2car[mapx;mapy;mapfn1;mapfn2] Identical to mapcar except mapfn1 is a function of two arguments and mapfn1[car[mapx];car[mapy]] is used to assemble the new list. Terminates when either mapx or mapy is exhausted. Note: CLISP (Section 23) provides a more general and complete facility for expressing iterative statements, e.g. (FOR X IN Y COLLECT (CADR X) WHEN (NUMBERP (CAR X)) UNTIL (NULL X)). ------------------------------------------------------------------------ 2 Note that since mapconc uses nconc to string the corresponding lists together, in this example, the original list will be clobbered, i.e., it would now be ((A B D E F G) C (D E F G) (G) H I). If this is an undesirable side effect, the functional argument to mapconc should return instead a top level copy, e.g., in this case, use (AND (LISTP Y) (APPEND Y)). 3 mapfn2 is still a function of one argument, and is applied twice on each iteration; mapfn2[mapx] gives the new mapx, mapfn2[mapy] the new mapy. cdr is used if mapfn2 is not supplied, i.e., is NIL. 11.3 maprint[lst;file;left;right;sep;pfn;lispxprintflg] is a general printing function. It cycles through lst applying pfn (or prin1 if pfn not given) to each element of lst. Between each application, maprint performs prinl of sep, or " " if sep=NIL. If left is given, it is printed (using prin1) initially; if right is given it is printed (using prinl) at the end. For example, maprint[x;NIL;%(;%)] is equivalent to prin1 for lists. To print a list with commas between each element and a final "." one could use maprint[x;T;NIL;%.;%,]. If lispxprintflg = T, lispxprinl is used for prinl (see Section 22). mapdl,searchpdl See Section 12. mapatoms See Section 5. every, some, notevery, notany See Section 5. Funarg function is a function of two arguments, fn, a function, and env is either NIL, a list of variables used freely by fn, a stack pointer, or an atom. If env is a list of variables, the value of function is an expression of the form (FUNARG fn pos), where pos is a stack pointer to a frame that contains the bindings of the variables on env at the time the call to function was evaluated. If env is a stack pointer, the 4 value of function is (FUNARG fn env). funarg is not a function itself. Like LAMBDA and NLAMBDA, it has meaning and is specially recognized by INTERLISP only in the context of applying a function to arguments. In other words, the expression 5 (FUNARG fn pos) is used exactly like a function. When a funarg is applied or is car of a form being eval'ed, the apply or eval takes place in the access environment specified by env (see Section 12). ------------------------------------------------------------------------ 4 If env is NIL, the value of function is simply fn, i.e., not a funarg expression. If env is an atom, it is evaluated and its value interpreted as described above. 5 LAMBDA, NLAMBDA, and FUNARG expressions are sometimes called "function objects" to distinguish them from functions, i.e., literal atoms which have function definitions. 11.4 For example, suppose a program wished to compute (FOO X (FUNCTION FIE)), and fie used y and z as free variables. If foo rebound y and z, fie would obtain the rebound values when it was applied from inside of foo. By evaluating instead (FOO X (FUNCTION FIE (Y Z))), foo would be called with (FUNARG FIE pos) as its second argument, where pos contained the bindings of y and z (at the time foo was called). Thus when fie was applied from inside of foo, it would "see" the original values of y and z. However, funarg is more than just a way of circumventing the clashing of variables. For example, a funarg expression can be returned as the value of a computation, and then used "higher up". Furthermore, if the function in a funarg expression sets any of the variables contained in the frame, only the frame would be changed. For example, suppose foo is defined as (LAMBDA (LST FN) (PROG (Y Z) (SETQ Y &) (SETQ Z &) ... (MAPC LIST FN) ...)) and (FOO X (FUNCTION FIE (Y Z))) is evaluated. If one application of fie (by the mapc in foo) changes y and z, then the next application of fie will obtain the changed values of y and z resulting from the previous application of fie, since both applications of fie come from the exact same funarg object, and hence use the exact same frame. The bindings of y and z bound inside of foo, and the bindings of y and z above foo would not be affected. In other words, the variable bindings contained in pos are a part of the function object, i.e., the funarg carries its environment with it. Thus by creating a funarg expression with function, a program can create a function object which has updateable binding(s) associated with the object which last between calls to it, but are only accessible through that instance of the function. For example, using the funarg device, a program could maintain two different instances of the same random number generator in different states, and run them independently. Example If foo is defined as (LAMBDA (X) (COND ((ZEROP A) X) (T (MINUS X)))) and and fie as (LAMBDA NIL (PROG (A) (SETQ A 2) (RETURN (FUNCTION FOO)))). then if we perform (SETQ A 0), (SETQ FUM (FIE)), the value of fum is FOO, and the value of (APPLY* FUM 3) is 3, because the value of A at the time foo is called is 0. However if fie were defined instead as (LAMBDA NIL (PROG (A) (SETQ A 2) (RETURN (FUNCTION FOO (A))))), the value of fum would be (FUNARG FOO pos) and so the value of (APPLY* FUM 3) would be -3, because the value of A seen by foo is the value A had when the funarg was created inside of fie, i.e., 2. 11.5