Trailing-Edge
-
PDP-10 Archives
-
decus_20tap1_198111
-
decus/20-0004/11lisp.doc
There are no other files named 11lisp.doc in the archive.
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