[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.
'((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.
(FROM VAR INIT FINAL STEP)
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.
(FOR VAR INIT NEXT)
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.
(ADJOIN EXP), (ADJOINQ EXP)
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.
(UNION EXP), (UNIONQ EXP)
are similar to JOIN, but only add an element to the list if it
is not already there. UNION tests with EQUAL, UNIONQ tests
(INTERSECTION EXP), (INTERSECTIONQ EXP)
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.
(SUM EXP), (PRODUCT EXP), (MAXIMIZE EXP), and (MINIMIZE EXP)
do the obvious. Synonyms are summing, maximizing, and
(MAXIMAL EXP1 EXP2), (MINIMAL EXP1 EXP2)
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)).
(WHILE EXP) and (UNTIL 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