The for
clauses for iterating over lists are much simpler than the arithmetic clauses. They support only two prepositional phrases, in
and on
.
A phrase of this form:
for
steps
(loop for i in (list 10 20 30 40) collect i) ==> (10 20 30 40)
Occasionally this clause is supplemented with a by
phrase, which specifies a function to use to move down the list. The default is CDR
but can be any function that takes a list and returns a sublist. For instance, you could collect every other element of a list with a loop like this:
(loop for i in (list 10 20 30 40) by #'cddr collect i) ==> (10 30)
An on
prepositional phrase is used to step
(loop for x on (list 10 20 30) collect x) ==> ((10 20 30) (20 30) (30))
This phrase too can take a by
preposition:
(loop for x on (list 10 20 30 40) by #'cddr collect x) ==> ((10 20 30 40) (30 40))
Looping over the elements of a vector (which includes strings and bit vectors) is similar to looping over the elements of a list except the preposition across
is used instead of in
.[236] For instance:
(loop for x across 'abcd' collect x) ==> (#a # #c #d)
Iterating over a hash table or package is slightly more complicated because hash tables and packages have different sets of values you might want to iterate over—the keys or values in a hash table and the different kinds of symbols in a package. Both kinds of iteration follow the same pattern. The basic pattern looks like this:
(loop for
For hash tables, the possible values for hash-keys
and hash-values
, which cause var
to be bound to successive values of either the keys or the values of the hash table. The
To iterate over a package, symbols
, present- symbols
, and external-symbols
, which cause FIND-PACKAGE
or a package object. Synonyms are also available for parts of the for
clause. In place of the
, you can use each
; you can use of
instead of in
; and you can write the hash-key
or symbol
).
Finally, since you'll often want both the keys and the values when iterating over a hash table, the hash table clauses support a using
subclause at the end of the hash table clause.
(loop for k being the hash-keys in h using (hash-value v) ...)
(loop for v being the hash-values in h using (hash-key k) ...)
Both of these loops will bind k
to each key in the hash table and v
to the corresponding value. Note that the first element of the using
subclause must be in the singular form.[237]
If none of the other for
clauses supports exactly the form of variable stepping you need, you can take complete control over stepping with an DO
loop but cast in a more Algolish syntax. The template is as follows:
(loop for
As usual, then
part to the clause, the DO
binding clause with no step form.
The for
clauses later in the loop. For instance:
(loop repeat 5
for x = 0 then y
for y = 1 then (+ x y)
collect y) ==> (1 2 4 8 16)
However, note that each for
clause is evaluated separately in the order it appears. So in the previous loop, on the second iteration x
is set to the value of y
before y
changes (in other words, 1
). But y
is then set to the sum of its old value (still 1
) and the new value of x
. If the order of the for
clauses is reversed, the results change.
(loop repeat 5
for y = 1 then (+ x y)
for x = 0 then y
collect y) ==> (1 1 2 4 8)
Often, however, you'll want the step forms for multiple variables to be evaluated before any of the variables is given its new value (similar to how DO
steps its variables). In that case, you can join multiple for
clauses by replacing all but the first for
with and
. You saw this formulation already in the LOOP
version of the Fibonacci computation in Chapter 7. Here's another variant, based on the two previous examples:
(loop repeat 5
for x = 0 then y
and y = 1 then (+ x y)
collect y) ==> (1 1 2 3 5)
While the main variables needed within a loop are usually declared implicitly in for
clauses, sometimes you'll need auxiliary variables, which you can declare with with
clauses.
with
The name with
clause contains an = value-form
part, the variable will be initialized, before the first iteration of the loop, to the value of
Multiple with
clauses can appear in a loop; each clause is evaluated independently in the order it appears and the value is assigned before proceeding to the next clause, allowing later variables to depend