Common Lisp the Language, 2nd Edition
Next: End-Test Control
Up: Loop
Previous: Loop
Constructs
Iteration control clauses allow you to direct loop iteration. The loop
keywords as
, for
, and repeat
designate iteration control clauses.
Iteration control clauses differ with respect to the specification of termination conditions and the initialization and stepping of loop variables. Iteration clauses by themselves do not cause the Loop Facility to return values, but they can be used in conjunction with value-accumulation clauses to return values (see section 26.8).
All variables are initialized in the loop prologue. The scope of the variable binding is lexical unless it is proclaimed special; thus, the variable can be accessed only by expressions that lie textually within the loop. Stepping assignments are made in the loop body before any other expressions are evaluated in the body.
The variable argument in iteration control clauses can be a destructuring list. A destructuring list is a tree whose non-null atoms are symbols that can be assigned a value (see section 26.12.2).
The iteration control clauses for
, as
, and
repeat
must precede any other loop clauses except
initially
, with
, and named
, since
they establish variable bindings. When iteration control clauses are
used in a loop, termination tests in the loop body are evaluated before
any other loop body code is executed.
If you use multiple iteration clauses to control iteration, variable
initialization and stepping occur sequentially by default. You can use
the and
construct to connect two or more iteration clauses
when sequential binding and stepping are not necessary. The iteration
behavior of clauses joined by and
is analogous to the
behavior of the Common Lisp macro do
relative to
do*
.
[X3J13 voted in March 1989 (LOOP-AND-DISCREPANCY) to correct a
minor inconsistency in the original syntactic specification for
loop
. Only for
and as
clauses
(not repeat
clauses) may be joined by the and
construct. The precise syntax is as follows.
for-as ::= {for | as} for-as-subclause {and for-as-subclause}*
for-as-subclause ::= for-as-arithmetic | for-as-in-list
| for-as-on-list | for-as-equals-then
| for-as-across | for-as-hash | for-as-package
for-as-arithmetic ::= var [type-spec] [{from | downfrom | upfrom} expr1 ]
[{to | downto | upto | below | above} expr2]
[by expr3]
for-as-in-list ::= var [type-spec] in expr1 [by step-fun]
for-as-on-list ::= var [type-spec] on expr1 [by step-fun]
for-as-equals-then ::= var [type-spec] = expr1 [then step-fun]
for-as-across ::= var [type-spec] across vector
for-as-hash ::= var [type-spec] being {each | the}
{hash-key | hash-keys | hash-value | hash-values}
{in | of} hash-table
[using ({hash-value | hash-key} other-var)]
for-as-package ::= var [type-spec] being {each | the}
for-as-package-keyword
{in | of} package
for-as-package-keyword ::= symbol | present-symbol | external-symbol
| symbols | present-symbols | external-symbols
This correction made for
and as
clauses
syntactically similar to with
clauses. I have changed all
examples in this chapter to reflect the corrected syntax.-GLS]
In the following example, the variable x
is stepped
before y
is stepped; thus, the value of y
reflects the updated value of x
:
(loop for x from 1 to 9
for y = nil then x
collect (list x y))
=> ((1 NIL) (2 2) (3 3) (4 4) (5 5) (6 6) (7 7) (8 8) (9 9))
In the following example, x
and y
are
stepped in parallel:
(loop for x from 1 to 9
and y = nil then x
collect (list x y))
=> ((1 NIL) (2 1) (3 2) (4 3) (5 4) (6 5) (7 6) (8 7) (9 8))
The for
and as
clauses iterate by using one
or more local loop variables that are initialized to some value and that
can be modified or stepped after each iteration. For these clauses,
iteration terminates when a local variable reaches some specified value
or when some other loop clause terminates iteration. At each iteration,
variables can be stepped by an increment or a decrement or can be
assigned a new value by the evaluation of an expression. Destructuring
can be used to assign initial values to variables during iteration.
The for
and as
keywords are synonyms and
may be used interchangeably. There are seven syntactic representations
for these constructs. In each syntactic description, the data type of
var can be specified by the optional type-spec
argument. If var is a destructuring list, the data type
specified by the type-spec argument must appropriately match
the elements of the list (see sections 26.12.1 and 26.12.2).
[Loop Clause]
for var [type-spec] [{from | downfrom | upfrom} expr1]
[{to | downto | upto | below | above} expr2]
[by expr3]
as var [type-spec] [{from | downfrom | upfrom} expr1]
[{to | downto | upto | below | above} expr2]
[by expr3]
[This is the first of seven for
/as
syntaxes.-GLS]
The for
or as
construct iterates from the
value specified by expr1 to the value specified by
expr2 in increments or decrements denoted by expr3.
Each expression is evaluated only once and must evaluate to a
number.
The variable var is bound to the value of expr1 in the first iteration and is stepped by the value of expr3 in each succeeding iteration, or by 1 if expr3 is not provided.
The following loop keywords serve as valid prepositions within this syntax.
from
The loop keyword from
marks the value from which stepping
begins, as specified by expr1. Stepping is incremental by
default. For decremental stepping, use above
or
downto
with expr2. For incremental stepping, the
default from
value is 0
.
downfrom
, upfrom
The loop keyword downfrom
indicates that the variable
var is decreased in decrements specified by expr3; the
loop keyword upfrom
indicates that var is
increased in increments specified by expr3.
to
The loop keyword to
marks the end value for stepping
specified in expr2. Stepping is incremental by default. For
decremental stepping, use downto
, downfrom
, or
above
with expr2.
downto
, upto
The loop keyword downto
allows iteration to proceed from a
larger number to a smaller number by the decrement expr3. The
loop keyword upto
allows iteration to proceed from a
smaller number to a larger number by the increment expr3. Since
there is no default for expr1 in decremental stepping, you must
supply a value with downto
.
below
, above
The loop keywords below
and above
are
analogous to upto
and downto
, respectively.
These keywords stop iteration just before the value of the variable
var reaches the value specified by expr2; the end
value of expr2 is not included. Since there is no default for
expr1 in decremental stepping, you must supply a value with
above
.
by
The loop keyword by
marks the increment or decrement
specified by expr3. The value of expr3 can be any
positive number. The default value is 1
.
At least one of these prepositions must be used with this syntax.
In an iteration control clause, the for
or
as
construct causes termination when the specified limit is
reached. That is, iteration continues until the value var is
stepped to the exclusive or inclusive limit specified by expr2.
The range is exclusive if expr3 increases or decreases
var to the value of expr2 without reaching that value;
the loop keywords below
and above
provide
exclusive limits. An inclusive limit allows var to
attain the value of expr2; to
,
downto
, and upto
provide inclusive limits.
A common convention is to use for
to introduce new
iterations and as
to introduce iterations that depend on a
previous iteration specification. [However, loop
does not
enforce this convention, and some of the examples below violate it.
De gustibus non disputandum est.-GLS]
Examples:
;;; Print some numbers.
(loop as i from 1 to 5
do (print i)) `;Prints 5 lines
1
2
3
4
5
=> NIL
;;; Print every third number.
(loop for i from 10 downto 1 by 3
do (print i)) `;Prints 4 lines
10
7
4
1
=> NIL
;;; Step incrementally from the default starting value.
(loop as i below 5
do (print i)) `;Prints 5 lines
0
1
2
3
4
=> NIL
[Loop Clause]
for
var [type-spec]
in
expr1 [by
step-fun]
as
var [type-spec]
in
expr1 [by
step-fun]
[This is the second of seven for
/as
syntaxes.-GLS]
This construct iterates over the contents of a list. It checks for
the end of the list as if using the Common Lisp function
endp
. The variable var is bound to the successive
elements of the list expr1 before each iteration. At the end of
each iteration, the function step-fun is called on the list and
is expected to produce a successor list; the default value for
step-fun is the cdr
function.
The for
or as
construct causes termination
when the end of the list is reached. The loop keywords in
and by
serve as valid prepositions in this syntax.
Examples:
;;; Print every item in a list.
(loop for item in '(1 2 3 4 5) do (print item)) `;Prints 5 lines
1
2
3
4
5
=> NIL
;;; Print every other item in a list.
(loop for item in '(1 2 3 4 5) by #'cddr
do (print item)) `;Prints 3 lines
1
3
5
=> NIL
;;; Destructure items of a list, and sum the x values
;;; using fixnum arithmetic.
(loop for (item . x) (t . fixnum)
in '((A . 1) (B . 2) (C . 3))
unless (eq item 'B) sum x)
=> 4
[Loop Clause]
for
var [type-spec]
on
expr1 [by
step-fun]
as
var [type-spec]
on
expr1 [by
step-fun]
[This is the third of seven for
/as
syntaxes.-GLS]
This construct iterates over the contents of a list. It checks for
the end of the list as if using the Common Lisp function
endp
. The variable var is bound to the successive
tails of the list expr1. At the end of each iteration, the
function step-fun is called on the list and is expected to
produce a successor list; the default value for step-fun is the
cdr
function.
The loop keywords on
and by
serve as valid
prepositions in this syntax. The for
or as
construct causes termination when the end of the list is reached.
Examples:
;;; Collect successive tails of a list.
(loop for sublist on '(a b c d)
collect sublist)
=> ((A B C D) (B C D) (C D) (D))
;;; Print a list by using destructuring with the loop keyword ON.
(loop for (item) on '(1 2 3)
do (print item)) `;Prints 3 lines
1
2
3
=> NIL
;;; Print items in a list without using destructuring.
(loop for item in '(1 2 3)
do (print item)) `;Prints 3 lines
1
2
3
=> NIL
[Loop Clause]
for
var [type-spec]
=
expr1 [then
expr2]
as
var [type-spec]
=
expr1 [then
expr2]
[This is the fourth of seven for
/as
syntaxes.-GLS]
This construct initializes the variable var by setting it to the result of evaluating expr1 on the first iteration, then setting it to the result of evaluating expr2 on the second and subsequent iterations. If expr2 is omitted, the construct uses expr1 on the second and subsequent iterations. When expr2 is omitted, the expanded code shows the following optimization:
;;; Sample original code:
(loop for x = expr1 then expr2 do (print x))
;;; The usual expansion:
(tagbody
(setq x expr1)
tag (print x)
(setq x expr2)
(go tag))
;;; The optimized expansion:
(tagbody
tag (setq x expr1)
(print x)
(go tag))
The loop keywords =
and then
serve as valid
prepositions in this syntax. This construct does not provide any
termination conditions.
Example:
;;; Collect some numbers.
(loop for item = 1 then (+ item 10)
repeat 5
collect item)
=> (1 11 21 31 41)
[Loop Clause]
for
var [type-spec]
across
vector
as
var [type-spec]
across
vector
[This is the fifth of seven for
/as
syntaxes.-GLS]
This construct binds the variable var to the value of each element in the array vector.
The loop keyword across
marks the array vector;
across
is used as a preposition in this syntax. Iteration
stops when there are no more elements in the specified array that can be
referenced.
Some implementations might use a [user-supplied-GLS] the
special form in the vector form to produce more efficient
code.
Example:
(loop for char across (the simple-string (find-message port))
do (write-char char stream))
[Loop Clause]
for var [type-spec] being {each | the}
{hash-key | hash-keys | hash-value | hash-values}
{in | of} hash-table [using ({hash-value | hash-key} other-var)]
as var [type-spec] being {each | the}
{hash-key | hash-keys | hash-value | hash-values}
{in | of} hash-table [using ({hash-value | hash-key} other-var)]
[This is the sixth of seven for
/as
syntaxes.-GLS]
This construct iterates over the elements, keys, and values of a hash table. The variable var takes on the value of each hash key or hash value in the specified hash table.
The following loop keywords serve as valid prepositions within this syntax.
being
The keyword being
marks the loop method to be used, either
hash-key
or hash-value
.
each
, the
For purposes of readability, the loop keyword each
should
follow the loop keyword being
when hash-key
or
hash-value
is used. The loop keyword the
is
used with hash-keys
and hash-values
.
hash-key
, hash-keys
These loop keywords access each key entry of the hash table. If the name
hash-value
is specified in a using
construct
with one of these loop methods, the iteration can optionally access the
keyed value. The order in which the keys are accessed is undefined;
empty slots in the hash table are ignored.
hash-value
, hash-values
These loop keywords access each value entry of a hash table. If the name
hash-key
is specified in a using
construct
with one of these loop methods, the iteration can optionally access the
key that corresponds to the value. The order in which the keys are
accessed is undefined; empty slots in the hash table are ignored.
using
The loop keyword using
marks the optional key or the keyed
value to be accessed. It allows you to access the hash key if iterating
over the hash values, and the hash value if iterating over the hash
keys.
in
, of
These loop prepositions mark the hash table hash-table.
Iteration stops when there are no more hash keys or hash values to be referenced in the specified hash table.
[Loop Clause]
for var [type-spec] being {each | the}
{symbol | present-symbol | external-symbol |
symbols | present-symbols | external-symbols}
{in | of} package
as var [type-spec] being {each | the}
{symbol | present-symbol | external-symbol |
symbols | present-symbols | external-symbols}
{in | of} package
[This is the last of seven for
/as
syntaxes.-GLS]
This construct iterates over the symbols in a package. The variable var takes on the value of each symbol in the specified package.
The following loop keywords serve as valid prepositions within this syntax.
being
The keyword being
marks the loop method to be used:
symbol
, present-symbol
, or
external-symbol
.
each
, the
For purposes of readability, the loop keyword each
should
follow the loop keyword being
when symbol
,
present-symbol
, or external-symbol
is used.
The loop keyword the
is used with symbols
,
present-symbols
, and external-symbols
.
present-symbol
, present-symbols
These loop methods iterate over the symbols that are present but not
external in a package. The package to be iterated over is specified in
the same way that package arguments to the Common Lisp function
find-package
are specified. If you do not specify the
package for the iteration, the current package is used. If you specify a
package that does not exist, an error is signaled.
symbol
, symbols
These loop methods iterate over symbols that are accessible from a given
package. The package to be iterated over is specified in the same way
that package arguments to the Common Lisp function
find-package
are specified. If you do not specify the
package for the iteration, the current package is used. If you specify a
package that does not exist, an error is signaled.
external-symbol
, external-symbols
These loop methods iterate over the external symbols of a package. The
package to be iterated over is specified in the same way that package
arguments to the Common Lisp function find-package
are
specified. If you do not specify the package for the iteration, the
current package is used. If you specify a package that does not exist,
an error is signaled.
in
, of
These loop prepositions mark the package package.
Iteration stops when there are no more symbols to be referenced in the specified package.
Example:
(loop for x being each present-symbol of "COMMON-LISP-USER"
do (print x)) `;Prints 7 lines in this example
COMMON-LISP-USER::IN
COMMON-LISP-USER::X
COMMON-LISP-USER::ALWAYS
COMMON-LISP-USER::FOO
COMMON-LISP-USER::Y
COMMON-LISP-USER::FOR
COMMON-LISP-USER::LUCID
=> NIL
[Loop Clause]
repeat
expr
The repeat
construct causes iteration to terminate after
a specified number of times. The loop body is executed n times,
where n is the value of the expression expr. The
expr argument is evaluated one time in the loop prologue. If
the expression evaluates to zero or to a negative number, the loop body
is not evaluated.
The clause repeat
n is roughly equivalent to a
clause such as
for internal-variable downfrom (- n 1) to 0
but, in some implementations, the repeat
construct might
be more efficient.
Examples:
(loop repeat 3 `;Prints 3 lines
do (format t "What I say three times is true~%"))
What I say three times is true
What I say three times is true
What I say three times is true
=> NIL
(loop repeat -15 `;Prints nothing
do (format t "What you see is what you expect~%"))
=> NIL
Next: End-Test Control
Up: Loop
Previous: Loop
Constructs
AI.Repository@cs.cmu.edu