29. "The Iteration Framework"
.setq loop-iteration-framework-section css-number
.setq loop-iteration-framework-page page
This section describes the way loop constructs
iterations. It is necessary if you will be writing your own iteration
paths, and may be useful in clarifying what loop does with its
input.
loop considers the act of stepping to have four
possible parts. Each iteration-driving clause has some or all of these
four parts, which are executed in this order:
pre-step-endtest
- This is an endtest which determines if it is safe to step to the next
value of the iteration variable.
steps
- Variables which get "stepped". This is internally manipulated as a
list of the form (var1 val1 var2 val2
...); all of those variables are stepped in parallel, meaning that
all of the vals are evaluated before any of the vars are
set.
post-step-endtest
- Sometimes you can't see if you are done until you step to the next
value; that is, the endtest is a function of the stepped-to value.
pseudo-steps
- Other things which need to be stepped. This is typically used for
internal variables which are more conveniently stepped here, or to
set up iteration variables which are functions of some internal
variable(s) which are actually driving the iteration. This is a list
like steps, but the variables in it do not get stepped in
parallel.
The above alone is actually insufficient in just about all
iteration driving clauses which loop handles. What is missing
is that in most cases the stepping and testing for the first time
through the loop is different from that of all other times. So, what
loop deals with is two four-tuples as above; one for the first
iteration, and one for the rest. The first may be thought of as
describing code which immediately precedes the loop in the prog,
and the second as following the body code--in fact, loop does
just this, but severely perturbs it in order to reduce code
duplication. Two lists of forms are constructed in parallel: one is
the first-iteration endtests and steps, the other the
remaining-iterations endtests and steps. These lists have dummy
entries in them so that identical expressions will appear in the same
position in both. When loop is done parsing all of the clauses,
these lists get merged back together such that corresponding identical
expressions in both lists are not duplicated unless they are "simple"
and it is worth doing.
Thus, one may get some duplicated code if one has
multiple iterations. Alternatively, loop may decide to use and
test a flag variable which indicates whether one iteration has been
performed. In general, sequential iterations have less overhead than
parallel iterations, both from the inherent overhead of stepping
multiple variables in parallel, and from the standpoint of potential
code duplication.
One other point which must be noted about parallel stepping is
that although the user iteration variables are guaranteed to be
stepped in parallel, the placement of the endtest for any particular
iteration may be either before or after the stepping. A notable case
of this is
| (loop for i from 1 to 3 and dummy = (print 'foo)
collect i)
=> (1 2 3)
|
but prints foo four times. Certain other constructs, such
as for var on, may or may not do this depending on the
particular construction.
This problem also means that it may not be safe to examine an
iteration variable in the epilogue of the loop form. As a general
rule, if an iteration driving clause implicitly supplies an endtest,
then one cannot know the state of the iteration variable when the loop
terminates. Although one can guess on the basis of whether the
iteration variable itself holds the data upon which the endtest is
based, that guess may be wrong. Thus,
| (loop for subl on expr
...
finally (f subl))
|
is incorrect, but
| (loop as frob = expr while (g frob)
...
finally (f frob))
|
is safe because the endtest is explicitly dissociated from the
stepping.
This document was generated
by Brad Parker on June, 13 2006
using texi2html