[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

PSL's FOR Macro

The PSL FOR macro description is not very long, so here it is:

(FOR . S:forms): any                                                      Macro

The arguments to FOR are clauses; each clause is itself a list of a keyword and
one or more arguments.  The clauses  may  introduce  local  variables,  specify
return  values  and  when the iteration should cease, have side-effects, and so
on.  Before going further, it is probably best to give some examples.

    (FOR (FROM I 1 10 2) (DO (PRINT I)))
            Prints the numbers 1 3 5 7 9

    (FOR (IN U '(A B C)) (DO (PRINT U)))
            Prints the letters A B C

    (FOR (ON U '(A B C)) (DO (PRINT U)))
            Prints the lists (A B C) (B C) and (C)

    Finally, the function
    (DE ZIP (X Y)
      (FOR (IN U X) (IN V Y)
            (COLLECT (LIST U V))))

produces a list of 2 element lists, each consisting of  the  the  corresponding
elements of the three lists X, Y and Z. For example, 

    (ZIP '(1 2 3 4) '(A B C) )


    ((1 a)(2 b)(3 c))

The iteration terminates as soon as one of the (IN ..) clauses is exhausted.

Note  that  the  (IN  ...  ),  (ON  ...) and (FROM ...) clauses introduce local
variables U, V or I, that are referred to in the action clause.

All the possible  clauses  are  described  below.    The  first  few  introduce
iteration  variables.    Most  of these also give some means of indicating when
iteration should cease.  For example, if a list being  mapped  over  by  an  IN
clause  is  exhausted, iteration must cease.  If several such clauses are given
in a FOR expression, iteration ceases when one  of  the  clauses  indicates  it
should, whether or not the other clauses indicate that it should cease.

(IN V1 V2)      assigns the variable V1 successive elements of the list V2.

                This  may  take an additional, optional argument: a function to
                be applied to the extracted element or  sublist  before  it  is
                assigned to the variable.  The following returns the sum of the
                lengths of all the elements of L. [Rather a kludge -- not  sure
                why this is here.  Perhaps it should come out again.]

                    (DE LENGTHS (L)
                      (FOR (IN N L LENGTH)
                    (COLLECT (LIST N N)))

                    is the same as

                    (DE LENGTHS (L)
                      (FOR (IN N L)
                          (LIST (LENGTH N) (LENGTH N))))

                but  only  calls  LENGTH  once.  Using  the  (WITH  ..) form to
                introduce a local LN may be clearer.

                    For example,
                     '((1 2 3 4 5)(a b c)(x y)))
                    ((5 5) (3 3) (2 2))

(ON V1 V2)      assigns the variable V1 successive CDRs of the list V2.

                is  a numeric iteration clause.  The variable is first assigned
                INIT, and then incremented by step  until  it  is  larger  than
                FINAL.  INIT, FINAL, and STEP are optional.  INIT and STEP both
                default to 1, and if FINAL is omitted the  iteration  continues
                until stopped by some other means.  To specify a STEP with INIT
                or FINAL omitted, or a FINAL with INIT omitted, place NIL  (the
                constant -- it cannot be an expression) in the appropriate slot
                to be omitted.  FINAL and STEP are only evaluated once.

                assigns  the variable INIT first, and subsequently the value of
                the expression NEXT.  INIT and NEXT may be omitted.  Note  that
                this is identical to the behavior of iterators in a DO.

(WITH V1 V2 ... Vn)
                introduces N locals, initialized to NIL.  In addition, each  Vi
                may  also  be  of  the  form  (VAR  INIT),  in which case it is
                initialized to INIT.

(DO S1 S2 ... Sn)
                causes the Si's to be evaluated at each iteration.

There  are  two  clauses  which  allow arbitrary code to be executed before the
first iteration, and after the last.

(INITIALLY S1 S2 ... Sn)
                causes  the  Si's  to be evaluated in the new environment (i.e.
                with the iteration variables bound  to  their  initial  values)
                before the first iteration.

(FINALLY S1 S2 ... Sn)
                causes the Si's  to  be  evaluated  just  before  the  function

The  next  few clauses build up return types.  Except for the RETURNS/RETURNING
clause, they may each take an additional argument which specifies that  instead
of  returning  the  appropriate  value,  it  is  accumulated  in  the specified
variable.  For example, an unzipper might be defined as 

    (DE UNZIP (L)
      (FOR (IN U L) (WITH X Y)
        (COLLECT (FIRST U) X)
        (COLLECT (SECOND U) Y)
        (RETURNS (LIST X Y))))

This is essentially the opposite of ZIP.  Given a list of 2 element  lists,  it
unzips  them  into  2 lists, and returns a list of those 2 lists.  For example,
(unzip '((1 a)(2 b)(3 c))) returns is ((1 2 3)(a b c)).

(RETURNS EXP)   causes the given  expression  to  be  the  value  of  the  FOR.
                Returning  is  synonymous  with  returns.    It  may  be  given
                additional arguments, in which case they are evaluated in order
                and the value of the last is returned (implicit PROGN).

(COLLECT EXP)   causes  the successive values of the expression to be collected
                into a list.  Each value is APPENDed to the end of the list.

                are similar to COLLECT, but a value is added to the result only
                if it is not already in the list.   ADJOIN  tests  with  EQUAL,
                ADJOINQ tests with EQ.

(CONC EXP)      causes the successive values to be NCONC'd together.

(JOIN EXP)      causes them to be appended.

                are similar to JOIN, but only add an element to the list if  it
                is  not  already  there.   UNION tests with EQUAL, UNIONQ tests
                with EQ.

                compute  the  set of elements that are in all the sets iterated
                over.  With INTERSECTION, elements are the same if EQUAL,  with
                INTERSECTIONQ they are the same if EQ.

(COUNT EXP)     returns the number of times EXP was non-NIL.

                do  the  obvious.    Synonyms  are  summing,  maximizing,   and

                are  more  general  than  maximize  and  minimize.      MAXIMAL
                determines  the  greatest  value  for  EXP2 over the iteration,
                returning the value of EXP1 rather than the value of EXP2.   As
                a  particular  case  it  is  possible to return the value of an
                iteration variable for which some function  attains  a  maximum
                (or  minimum)  value,  e.g.  (MAXIMAL  x (f x)).  As with other
                kinds of clauses, the user may "accumulate" the value  of  EXP1
                into  a  variable  by supplying a third expression which is the
                name of a variable.

(ALWAYS EXP)    returns T if EXP is non-NIL on each iteration.  If EXP is  ever
                NIL, the loop terminates immediately, no epilogue code, such as
                that introduced by finally is run, and NIL is returned.

(NEVER EXP)     is equivalent to (ALWAYS (NOT EXP)).

                Explicit  tests  for  the  end  of  the loop may be given using
                (WHILE EXP).  The loop terminates if EXP  becomes  NIL  at  the
                beginning  of  an  iteration.    (UNTIL EXP)  is  equivalent to
                (WHILE (NOT EXP)).    Both  WHILE  and  UNTIL  may   be   given
                additional  arguments;  (WHILE E1 E2 ... En)  is  equivalent to
                (WHILE (AND E1 E2 ... En))    and    (UNTIL E1 E2 ... En)    is
                equivalent to (UNTIL (OR E1 E2 ... En)).

(WHEN EXP)      causes a jump to the next iteration if EXP is NIL.

(UNLESS EXP)    is equivalent to (WHEN (NOT EXP)).

FOR  is  a general iteration construct similar in many ways to the Lisp Machine
and  MACLISP  LOOP  construct,  and  the  earlier  Interlisp  CLISP   iteration
construct.    FOR,  however,  is  considerably  simpler,  far more "lispy", and
somewhat less powerful.

All variable binding/updating still precedes any tests or  other  code.    Also
note  that  all  WHEN  or  UNLESS clauses apply to all action clauses, not just
subsequent ones.  This fixed order of evaluation makes FOR less  powerful  than
LOOP, but also keeps it considerably simpler.  The basic order of evaluation is

   1. bind variables to initial values (computed in the outer environment)

   2. execute prologue (i.e., INITIALLY clauses)

   3. while none of the termination conditions are satisfied:

         a. check  conditionalization clauses (WHEN and UNLESS), and start
            next iteration if all are not satisfied.

         b. perform body, collecting into variables as necessary

         c. next iteration

   4. (after a termination condition is satisfied)  execute  the  epilogue
      (i.e., FINALLY clauses)

FOR  does all variable binding/updating in parallel.  There is a similar macro,
FOR*, which does it sequentially.

(FOR* . S:forms): any                                                     Macro