25. "Clauses"
Internally, loop constructs a prog which includes
variable bindings, pre-iteration (initialization) code,
post-iteration (exit) code, the body of the iteration, and stepping
of variables of iteration to their next values (which happens on
every iteration after executing the body).
A clause consists of the keyword symbol and any Lisp
forms and keywords which it deals with. For example,
| (loop for x in l do (print x)),
|
contains two clauses, "7for x in l" and "7do (print x)".
Certain of the parts of the clause will be described as being
expressions, e.g. (print x) in the above. An expression can
be a single Lisp form, or a series of forms implicitly collected with
progn. An expression is terminated by the next following atom,
which is taken to be a keyword. This syntax allows only the first
form in an expression to be atomic, but makes misspelled keywords
more easily detectable.
loop uses print-name equality to compare keywords so
that loop forms may be written without package prefixes; in
Lisp implementations that do not have packages, eq is used for
comparison.
Bindings and iteration variable steppings may be performed
either sequentially or in
parallel, which affects how the stepping of one iteration variable
may depend on the value of another. The syntax for distinguishing
the two will be described with the corresponding clauses. When a set
of things is "in parallel", all of the bindings produced will be
performed in parallel by a single lambda binding. Subsequent
bindings will be performed inside of that binding environment.
25.1 "Iteration-Driving Clauses"
These clauses all create a variable of iteration, which
is bound locally to the loop and takes on a new value on each
successive iteration. Note that if more than one iteration-driving
clause is used in the same loop, several variables are created which
all step together through their values; when any of the iterations
terminates, the entire loop terminates. Nested iterations are not
generated; for those, you need a second loop form in the body of
the loop. In order to not produce strange interactions, iteration
driving clauses are required to precede any clauses which produce
"body" code: that is, all except those which produce prologue or
epilogue code (initially and finally), bindings
(with), the named clause, and the iteration termination
clauses (while and until).
Clauses which drive the iteration may be arranged to perform
their testing and stepping either in series or in parallel. They are
by default grouped in series, which allows the stepping computation of
one clause to use the just-computed values of the iteration variables
of previous clauses. They may be made to step "in parallel", as is
the case with the do special form, by "joining" the iteration
clauses with the keyword and. The form this typically takes is
something like
| (loop ... for x = (f) and for y = init then (g x) ...)
|
which sets x to (f) on every iteration, and binds y
to the value of init for the first iteration, and on every
iteration thereafter sets it to (g x), where x still has
the value from the previous iteration. Thus, if the calls to
f and g are not order-dependent, this would be best
written as
| (loop ... for y = init then (g x) for x = (f) ...)
|
because, as a general rule, parallel stepping has more overhead than
sequential stepping. Similarly, the example
| (loop for sublist on some-list
and for previous = 'undefined then sublist
...)
|
which is equivalent to the do construct
| (do ((sublist some-list (cdr sublist))
(previous 'undefined sublist))
((null sublist) ...)
...)
|
in terms of stepping, would be better written as
| (loop for previous = 'undefined then sublist
for sublist on some-list
...)
|
When iteration driving clauses are joined with and, if
the token following the and is not a keyword which introduces an
iteration driving clause, it is assumed to be the same as the keyword
which introduced the most recent clause; thus, the above example
showing parallel stepping could have been written as
| (loop for sublist on some-list
and previous = 'undefined then sublist
...)
|
The order of evaluation in iteration-driving clauses is that
those expressions which are only evaluated once are evaluated in order
at the beginning of the form, during the variable-binding phase, while
those expressions which are evaluated each time around the loop are
evaluated in order in the body.
One common and simple iteration driving clause is
repeat:
repeat expression
- .keyword_index repeat
This evaluates expression (during the variable binding phase),
and causes the loop to iterate that many times.
expression is expected to evaluate to a fixnum. If
expression evaluates to a zero or negative result, the body code
will not be executed.
All remaining iteration driving clauses are subdispatches of
the keyword for, which is synonomous with as.
In all of them a variable of iteration is specified. Note that,
in general, if an iteration driving clause implicitly supplies an
endtest, the value of this iteration variable as the loop is exited
(i.e., when the epilogue code is run) is undefined. (This is
discussed in more detail in section
(loop-iteration-framework-section).)
Here are all of the varieties of for clauses. Optional
parts are enclosed in curly brackets. The data-types as used
here are discussed fully in section (loop-data-type-section).
for var {data-type
in expr1 {by expr2}}
- .keyword_index for
This iterates over each of the elements in the list expr1. If
the by subclause is present, expr2 is evaluated once
on entry to the loop
to supply the function to be used to fetch successive sublists,
instead of cdr.
for var {data-type
on expr1 {by expr2}}
- .keyword_index for
This is like the previous for format, except that var is
set to successive sublists of the list instead of successive elements.
Note that since var will always be a list, it is
not meaningful to specify a data-type unless var is
a destructuring pattern, as described in the section on
destructuring, (loop-destructuring-page). Note also that
loop uses a null rather than an atom test to
implement both this and the preceding clause.
for var {data-type
= expr}
- .keyword_index for
On each iteration, expr is evaluated and var is set to the result.
for var {data-type
= expr1 then expr2}
- .keyword_index for
var is bound to expr1 when the loop is entered, and set to
expr2 (re-evaluated) at all but the first iteration. Since
expr1 is evaluated during the binding phase, it cannot reference
other iteration variables set before it; for that, use the following:
for var {data-type
first expr1 then expr2}
- .keyword_index for
This sets var to expr1 on the first iteration, and to
expr2 (re-evaluated) on each succeeding iteration. The
evaluation of both expressions is performed inside of the
loop binding environment, before the loop body. This
allows the first value of var to come from the first value of
some other iteration variable, allowing such constructs as
| (loop for term in poly
for ans first (car term) then (gcd ans (car term))
finally (return ans))
|
for var {data-type
from expr1 {to expr2} {by expr3}}
- .keyword_index for
@setq loop-arithmetic-stepping page
This performs numeric iteration. var is initialized to
expr1, and on each succeeding iteration is incremented by
expr3 (default 1). If the to phrase is given, the
iteration terminates when var becomes greater than expr2.
Each of the expressions is evaluated only once, and the to and
by phrases may be written in either order. downto may be
used instead of to, in which case var is decremented by
the step value, and the endtest is adjusted accordingly. If
below is used instead of to, or above instead of
downto, the iteration will be terminated before expr2 is
reached, rather than after. Note that the to variant
appropriate for the direction of stepping must be used for the endtest
to be formed correctly; i.e. the code will not work if expr3
is negative or zero. If no limit-specifying clause is given, then the
direction of the stepping may be specified as being decreasing by
using downfrom instead of from. upfrom may also be
used instead of from; it forces the stepping direction to be
increasing. The data-type defaults to fixnum.
for var {data-type
being expr and its path ...}
for var {data-type
being {each|the} path ...}
- .keyword_index for
This provides a user-definable iteration facility. path names
the manner in which the iteration is to be performed. The ellipsis
indicates where various path dependent preposition/expression pairs
may appear. See the section on Iteration Paths
((iteration-path-page)) for complete documentation.
25.2 "Bindings"
.setq with-clause page
.keyword_index with
The with keyword may be used to establish initial
bindings, that is, variables which are local to the loop but are only
set once, rather than on each iteration. The with clause looks like:
| with var1 {data-type} {= expr1}
{and var2 {data-type} {= expr2}}...
|
If no expr is given, the variable is initialized to the
appropriate value for its data type, usually nil.
with bindings linked by and are performed in
parallel; those not linked are performed sequentially. That is,
| (loop with a = (foo) and b = (bar) and c
...)
|
binds the variables like
| ((lambda (a b c) ...)
(foo) (bar) nil)
|
whereas
| (loop with a = (foo) with b = (bar a) with c ...)
|
binds the variables like
| ((lambda (a)
((lambda (b)
((lambda (c) ...)
nil))
(bar a)))
(foo))
|
All expr's in with clauses are evaluated in the order they
are written, in lambda expressions surrounding the generated
prog. The loop expression
| (loop with a = xa and b = xb
with c = xc
for d = xd then (f d)
and e = xe then (g e d)
for p in xp
with q = xq
...)
|
produces the following binding contour, where t1 is a
loop-generated temporary:
| ((lambda (a b)
((lambda (c)
((lambda (d e)
((lambda (p t1)
((lambda (q) ...)
xq))
nil xp))
xd xe))
xc))
xa xb)
|
Because all expressions in with clauses are evaluated during the
variable binding phase, they are best placed near the front of the
loop form for stylistic reasons.
For binding more than one variable with no particular
initialization, one may use the construct
| with variable-list {data-type-list} {and ...}
|
as in
| with (i j k t1 t2) (fixnum fixnum fixnum) ...
|
A slightly shorter way of writing this is
| with (i j k) fixnum and (t1 t2) ...
|
These are cases of destructuring which loop handles
specially; destructuring and data type keywords are discussed in
sections (loop-destructuring-section) and
(loop-data-type-section).
Occasionally there are various implementational reasons
@setq loop-nodeclare-clause page
for a variable not to be given a local type declaration. If
this is necessary, the nodeclare clause may be used:
nodeclare variable-list
- The variables in variable-list are noted by loop as not
requiring local type declarations. Consider the following:
| (declare (special k) (fixnum k))
(defun foo (l)
(loop for x in l as k fixnum = (f x) ...))
|
If k did not have the fixnum data-type keyword given for
it, then loop would bind it to nil, and some compilers
would complain. On the other hand, the fixnum keyword also
produces a local fixnum declaration for k; since k
is special, some compilers will complain (or error out). The solution
is to do:
| (defun foo (l)
(loop nodeclare (k)
for x in l as k fixnum = (f x) ...))
|
which tells loop not to make that local declaration. The
nodeclare clause must come before any reference to the
variables so noted. Positioning it incorrectly will cause this clause
to not take effect, and may not be diagnosed.
25.3 "Entrance and Exit"
initially expression
- .keyword_index initially
This puts expression into the prologue of the iteration. It
will be evaluated before any other initialization code other than the
initial bindings. For the sake of good style, the initially
clause should therefore be placed after any with clauses but
before the main body of the loop.
finally expression
- .keyword_index finally
.setq loop-finally-clause page
This puts expression into the epilogue of the loop, which is
evaluated when the iteration terminates (other than by an explicit
return). For stylistic reasons, then, this clause should appear
last in the loop body. Note that certain clauses may generate code
which terminates the iteration without running the epilogue code;
this behavior is noted with those clauses. Most notable of these are
those described in the section (aggregated-boolean-tests-section),
Aggregated Boolean Tests. This clause may be used to cause the loop
to return values in a non-standard way:
| (loop for n in l
sum n into the-sum
count t into the-count
finally (return (quotient the-sum the-count)))
|
25.4 "Side Effects"
.setq side-effects-section css-number
do expression
doing expression
- .keyword_index do doing
expression is evaluated each time through the loop, as shown in
the print-elements-of-list example on
(print-elements-of-list-example).
25.5 "Values"
.setq values-section css-number
The following clauses accumulate a return value for the
iteration in some manner. The general form is
| type-of-collection expr {data-type} {into var}
|
where type-of-collection is a loop keyword, and expr
is the thing being "accumulated" somehow. If no into is
specified, then the accumulation will be returned when the loop
terminates. If there is an into, then when the epilogue of the
loop is reached, var (a variable automatically bound
locally in the loop) will have been set to the accumulated
result and may be used by the epilogue code. In this way, a user may
accumulate and somehow pass back multiple values from a single
loop, or use them during the loop. It is safe to reference
these variables during the loop, but they should not be modified
until the epilogue code of the loop is reached.
For example,
| (loop for x in list
collect (foo x) into foo-list
collect (bar x) into bar-list
collect (baz x) into baz-list
finally (return (list foo-list bar-list baz-list)))
|
has the same effect as
| (do ((g0001 list (cdr g0001))
(x) (foo-list) (bar-list) (baz-list))
((null g0001)
(list (nreverse foo-list)
(nreverse bar-list)
(nreverse baz-list)))
(setq x (car g0001))
(setq foo-list (cons (foo x) foo-list))
(setq bar-list (cons (bar x) bar-list))
(setq baz-list (cons (baz x) baz-list)))
|
except that loop arranges to form the lists in the correct
order, obviating the nreverses at the end, and allowing the
lists to be examined during the computation.
collect expr {into var
}
collecting ...
- .keyword_index collect collecting
.setq collect-clause page
This causes the values of expr on each iteration to be collected
into a list.
nconc expr {into var
}
nconcing ...
append ...
appending ...
- .keyword_index nconc nconcing append appending
These are like collect, but the results are nconced or
appended together as appropriate.
| (loop for i from 1 to 3
nconc (list i (* i i)))
=> (1 1 2 4 3 9)
|
count expr {into var
{data-type}}
counting ...
- .keyword_index count counting
If expr evaluates non-nil, a counter is incremented.
The data-type defaults to fixnum.
sum expr {data-type
{into var}}
summing ...
- .keyword_index sum summing
Evaluates expr on each iteration, and accumulates the sum of all
the values. data-type defaults to
number, which for all practical purposes is notype. Note
that specifying data-type implies that both the sum and
the number being summed (the value of expr) will be of that type.
maximize expr {data-type
{into var}}
minimize ...
- .keyword_index maximize minimize
Computes the maximum (or minimum) of expr over all
iterations. data-type defaults to number. Note that if
the loop iterates zero times, or if conditionalization prevents the
code of this clause from being executed, the result will be
meaningless. If loop can determine that the arithmetic being
performed is not contagious (by virtue of data-type being
fixnum, flonum, or small-flonum), then it may choose
to code this by doing an arithmetic comparison rather than calling
either max or min. As with the sum clause,
specifying data-type implies that both the result of the
max or min operation and the value being maximized or
minimized will be of that type.
Not only may there be multiple accumulations in a
loop, but a single accumulation may come from multiple
places within the same loop form. Obviously, the types of
the collection must be compatible. collect, nconc, and
append may all be mixed, as may sum and count, and
maximize and minimize. For example,
| (loop for x in '(a b c) for y in '((1 2) (3 4) (5 6))
collect x
append y)
=> (a 1 2 b 3 4 c 5 6)
|
.group
The following computes the average of the entries in the list
list-of-frobs:
| (loop for x in list-of-frobs
count t into count-var
sum x into sum-var
finally (return (quotient sum-var count-var)))
|
.end_group
25.6 "Endtests"
.topic terminating the iteration
The following clauses may be used to provide additional
control over when the iteration gets terminated, possibly causing
exit code (due to finally) to be performed and possibly returning
a value (e.g., from collect).
while expr
- .keyword_index while
If expr evaluates to nil, the loop is exited, performing
exit code (if any), and returning any accumulated value. The
test is placed in the body of the loop where it is written. It may
appear between sequential for clauses.
until expr
- .keyword_index until
Identical to while (not expr).
This may be needed, for example, to step through a strange
data structure, as in
| (loop until (top-of-concept-tree? concept)
for concept = expr then (superior-concept concept)
...)
|
Note that the placement of the while clause before the for
clause is valid in this case because of the definition of this
particular variant of for, which binds concept to
its first value rather than setting it from inside the loop.
The following may also be of use in terminating the iteration:
- Macro: loop-finish
- (loop-finish) causes the iteration to terminate "normally", the
same as implicit termination by an iteration driving clause, or by the
use of while or until--the epilogue code (if any) will be
run, and any implicitly collected result will be returned as the value
of the loop.
For example,
| (loop for x in '(1 2 3 4 5 6)
collect x
do (cond ((= x 4) (loop-finish))))
=> (1 2 3 4)
|
This particular example would be better written as until (= x 4)
in place of the do clause.
25.7 "Aggregated Boolean Tests"
.setq aggregated-boolean-tests-section css-number
All of these clauses perform some test, and may immediately
terminate the iteration depending on the result of that test.
always expr
- .keyword_index always
Causes the loop to return t if expr always evaluates
non-null. If expr evaluates to nil, the loop
immediately returns nil, without running the epilogue code (if
any, as specified with the finally clause); otherwise, t
will be returned when the loop finishes, after the epilogue code has
been run.
never expr
- .keyword_index never
Causes the loop to return t if expr never evaluates
non-null. This is equivalent to always (not expr).
thereis expr
- .keyword_index thereis
If expr evaluates non-nil, then the iteration is
terminated and that value is returned, without running the epilogue
code.
25.8 "Conditionalization"
.topic conditionalizing clause(s)
These clauses may be used to "conditionalize" the following
clause. They may precede any of the side-effecting or value-producing
clauses, such as do, collect, always, or
return.
when expr
if expr
- .keyword_index when if
If expr evaluates to nil, the following clause will be
skipped, otherwise not.
unless expr
- .keyword_index unless
This is equivalent to when (not expr)).
Multiple conditionalization clauses may appear in sequence.
If one test fails, then any following tests in the immediate sequence,
and the clause being conditionalized, are skipped.
Multiple clauses may be conditionalized under the same test by
joining them with and, as in
| (loop for i from a to b
when (zerop (remainder i 3))
collect i and do (print i))
|
which returns a list of all multiples of 3 from a to
b (inclusive) and prints them as they are being collected.
If-then-else conditionals may be written using the else keyword, as in
| (loop for i from a to b
when (oddp i)
collect i into odd-numbers
else collect i into even-numbers)
|
Multiple clauses may appear in an else-phrase, using and to join them
in the same way as above.
Conditionals may be nested. For example,
| (loop for i from a to b
when (zerop (remainder i 3))
do (print i)
and when (zerop (remainder i 2))
collect i)
|
returns a list of all multiples of 6 from a to b,
and prints all multiples of 3 from a to b.
When else is used with nested conditionals, the "dangling else"
ambiguity is resolved by matching the else with the innermost when
not already matched with an else. Here is a complicated example.
| (loop for x in l
when (atom x)
when (memq x *distinguished-symbols*)
do (process1 x)
else do (process2 x)
else when (memq (car x) *special-prefixes*)
collect (process3 (car x) (cdr x))
and do (memoize x)
else do (process4 x))
|
Useful with the conditionalization clauses is the return
clause, which causes an explicit return of its "argument" as
the value of the iteration, bypassing any epilogue code. That is,
is equivalent to
| when expr1 do (return expr2)
|
Conditionalization of one of the "aggregated boolean value"
clauses simply causes the test which would cause the iteration to
terminate early not to be performed unless the condition succeeds.
For example,
| (loop for x in l
when (significant-p x)
do (print x) (princ "is significant.")
and thereis (extra-special-significant-p x))
|
does not make the extra-special-significant-p check unless the
significant-p check succeeds.
.group
The format of a conditionalized clause is typically something
like
If expr2 is the keyword it, then a variable is generated to
hold the value of expr1, and that variable gets substituted for
expr2. Thus, the composition
is equivalent to the clause
and one may collect all non-null values in an iteration by saying
| when expression collect it
|
If multiple clauses are joined with and, the it keyword
may only be used in the first. If multiple whens,
unlesses, and/or ifs occur in sequence, the value
substituted for it will be that of the last test performed.
The it keyword is not recognized in an else-phrase.
.end_group
25.9 "Miscellaneous Other Clauses"
named name
- .keyword_index named
This gives the prog which loop generates a name of
name, so that one may use the return-from form to return
explicitly out of that particular loop:
| (loop named sue
...
do (loop ... do (return-from sue value) ...)
...)
|
The return-from form shown causes value to be immediately
returned as the value of the outer loop. Only one name may be
given to any particular loop construct.
This feature does not exist in the Maclisp version of loop, since
Maclisp does not support "named progs".
return expression
- .keyword_index return
Immediately returns the value of expression as the value of the
loop, without running the epilogue code. This is most useful with
some sort of conditionalization, as discussed in the previous
section. Unlike most of the other clauses, return is not
considered to "generate body code", so it is allowed to occur between
iteration clauses, as in
| (loop for entry in list
when (not (numberp entry))
return (error ...)
as frob = (times entry 2)
...)
|
If one instead desires the loop to have some return value when it
finishes normally, one may place a call to the return function in the
epilogue (with the finally clause, (loop-finally-clause)).
This document was generated
by Brad Parker on June, 13 2006
using texi2html