The loop
and format
commands are powerful and hacker-friendly. Though most of the functionality they offer is available elsewhere in the Lisp language, these highly specialized commands are worth learning if you like terse code. We’ll look at loop
in this chapter. The next chapter covers format
.
Any type of looping you would ever want to do inside a computer program can be accomplished with the loop
macro. Here’s a simple example:
>(loop for i
below 5
sum i)
10
This code adds together the natural numbers below 5, like this:
0 + 1 + 2 + 3 + 4 = 10 |
You can see that this loop
command doesn’t work in the way a proper Lisp command should. First of all, it’s parenthetically challenged. Never before have we had seven tokens in a row without parentheses!
What makes it even less Lispy is that some of these extra tokens (for
, below
, and sum
) appear to have special meanings. Recall from Chapter 3 that the first token in a form (the one immediately after the opening parenthesis) is typically what decides the basic behavior of the code, while the rest of the form contains parameters. Within the loop
macro, several of these “magic tokens” fundamentally affect the loop
’s behavior. Here’s what they mean:
for
allows you to declare a variable (in this case, named i
) that iterates through a range of values. By default, it will count through the integers starting at zero.
below
tells the for
construct to halt when it reaches the specified value (in this case, 5
), excluding the value itself.
sum
adds together all values of a given expression (in this case, the expression is just i
) and makes the loop
return that number.
The loop
macro has a veritable cornucopia of special tokens that make just about any kind of behavior possible. Let’s look at some of the possibilities.
By using from
and to
clauses, you can make the for
construct count through any specific range of integers:
>(loop for i
from 5
to 10
sum i)
45
In the following example, we iterate through values in a list using the in
token:
>(loop for i
in '(100 20 3)
sum i)
123
The do
token takes an arbitrary expression and executes it inside the loop
:
>(loop for i
below 5
do (print i))
0 1 2 3 4
The when
token lets you run the following part of the loop
only as needed:
>(loop for i
below 10
when (oddp i)
sum i)
25
Notice that only the sum of the odd numbers is returned.
The following loop
uses several new tricks:
>(loop for i
from 0
do (print i)
when (= i 5)
return 'falafel)
0 1 2 3 4 5 FALAFEL
Notice that there’s nothing in the for
part of the loop
that tells it to stop counting numbers—it goes from zero off to infinity. However, once we reach 5
, the when
clause triggers the loop to immediately return the value 'falafel
.
The collect
clause lets you return more than one item from the loop
, in the form of a list. This command is useful when you need to modify each item in a list, as in the following example:
>(loop for i
in '(2 3 4 5 6)
collect (* i i))
(4 9 16 25 36)
It’s possible for a loop
macro to have more than one for
clause. Consider the following example:
(loop for x below 10 for y below 10 collect (+ x y))
How many numbers do you think will be returned as a result? There are two possibilities: Either it increments x
and y
at the same time and returns a list of 10 items, or it iterates x
and y
in a nested fashion and returns 100 numbers. The answer is the former:
>(loop for x below 10
for y below 10
collect (+ x y))
(0 2 4 6 8 10 12 14 16 18)
As you can see, both numbers incremented at the same time between 0 and 9.
If there are multiple for
clauses in a Common Lisp loop
, each one will be checked, and the loop
will stop when any one of the clauses runs out of values. This means that for
clauses do not loop
independently across multiple looping variables, so if you loop
on two ranges of 10 values each, it will still just loop
10 times.
However, sometimes you want to generate the Cartesian product between multiple ranges. In other words, you want a loop
to run once for every possible combination of two or more ranges. To accomplish this, you need to use nested loops for x
and y
:
>(loop for x below 10
collect (loop for y below 10
collect (+ x y)))
((0 1 2 3 4 5 6 7 8 9) (1 2 3 4 5 6 7 8 9 10) (2 3 4 5 6 7 8 9 10 11) (3 4 5 6 7 8 9 10 11 12) (4 5 6 7 8 9 10 11 12 13) (5 6 7 8 9 10 11 12 13 14) (6 7 8 9 10 11 12 13 14 15) (7 8 9 10 11 12 13 14 15 16) (8 9 10 11 12 13 14 15 16 17) (9 10 11 12 13 14 15 16 17 18))
In this case, we’ve created 10 lists of 10 items each, loop
ing for a total of 100 items.
Also, notice that using a for
variable starting at zero, such as the i
variable in the following example, provides a clean way to track the index number of items in a list:
>(loop for i
from 0
for day
in '(monday tuesday wednesday thursday friday saturday sunday)
collect (cons i day))
((0 . MONDAY) (1 . TUESDAY) (2 . WEDNESDAY) (3 . THURSDAY) (4 . FRIDAY) (5 . SATURDAY) (6 . SUNDAY))
You might think we’ve covered every conceivable variation of looping at this point. If so, you are gravely mistaken. Behold! The Periodic Table of the Loop Macro!
The individual examples we’ve discussed so far give only the briefest hint of the full capabilities of loop
. But fear not! You now have the world’s first and only Periodic Table of the Loop Macro. Just tape it to your monitor, glue it to your wallet, or laser-etch it directly into your retina, and you’ll be guaranteed to reach loop
proficiency in no time!
Almost every legal command that can be used in a loop
macro is covered by the periodic table. It shows how to manipulate hash tables and arrays, and perform special looping operations. Each square in the periodic table contains an example. If you run the example, you should be able to figure out the behavior of the given command.
18.216.151.164