Google
 

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