((= 10 n) cur))
Finally, the next loop demonstrates a DO
loop that binds no variables. It loops while the current time is less than the value of a global variable, printing 'Waiting' once a minute. Note that even with no loop variables, you still need the empty variables list.
(do ()
((> (get-universal-time) *some-future-date*))
(format t 'Waiting~%')
(sleep 60))
For the simple cases you have DOLIST
and DOTIMES
. And if they don't suit your needs, you can fall back on the completely general DO
. What more could you want?
Well, it turns out a handful of looping idioms come up over and over again, such as looping over various data structures: lists, vectors, hash tables, and packages. Or accumulating values in various ways while looping: collecting, counting, summing, minimizing, or maximizing. If you need a loop to do one of these things (or several at the same time), the LOOP
macro may give you an easier way to express it.
The LOOP
macro actually comes in two flavors—
(loop
The forms in body are evaluated each time through the loop, which will iterate forever unless you use RETURN
to break out. For example, you could write the previous DO
loop with a simple LOOP
.
(loop
(when (> (get-universal-time) *some-future-date*)
(return))
(format t 'Waiting~%')
(sleep 60))
The extended LOOP
is quite a different beast. It's distinguished by the use of certain LOOP
language. At least one of Common Lisp's original designers hated it. LOOP
's detractors complain that its syntax is totally un-Lispy (in other words, not enough parentheses). LOOP
's fans counter that that's the point: complicated looping constructs are hard enough to understand without wrapping them up in DO
's cryptic syntax. It's better, they say, to have a slightly more verbose syntax that gives you some clues what the heck is going on.
For instance, here's an idiomatic DO
loop that collects the numbers from 1 to 10 into a list:
(do ((nums nil) (i 1 (1+ i)))
((> i 10) (nreverse nums))
(push i nums)) ==> (1 2 3 4 5 6 7 8 9 10)
A seasoned Lisper won't have any trouble understanding that code—it's just a matter of understanding the basic form of a DO
loop and recognizing the PUSH
/NREVERSE
idiom for building up a list. But it's not exactly transparent. The LOOP
version, on the other hand, is almost understandable as an English sentence.
(loop for i from 1 to 10 collecting i) ==> (1 2 3 4 5 6 7 8 9 10)
The following are some more examples of simple uses of LOOP
. This sums the first ten squares:
(loop for x from 1 to 10 summing (expt x 2)) ==> 385
This counts the number of vowels in a string:
(loop for x across 'the quick brown fox jumps over the lazy dog'
counting (find x 'aeiou')) ==> 11
This computes the eleventh Fibonacci number, similar to the DO
loop used earlier:
(loop for i below 10
and a = 0 then b
and b = 1 then (+ b a)
finally (return a))
The symbols across
, and
, below
, collecting
, counting
, finally
, for
, from
, summing
, then
, and to
are some of the loop keywords whose presence identifies these as instances of the extended LOOP
.[92]
I'll save the details of LOOP
for Chapter 22, but it's worth noting here as another example of the way macros can be used to extend the base language. While LOOP
provides its own language for expressing looping constructs, it doesn't cut you off from the rest of Lisp. The loop keywords are parsed according to loop's grammar, but the rest of the code in a LOOP
is regular Lisp code.
And it's worth pointing out one more time that while the LOOP
macro is quite a bit more complicated than macros such as WHEN
or UNLESS
, it
With that I'll conclude our tour of the basic control-construct macros. Now you're ready to take a closer look at how to define your own macros.
8. Macros: Defining Your Own
Now it's time to start writing your own macros. The standard macros I covered in the previous chapter hint at some of the things you can do with macros, but that's just the beginning. Common Lisp doesn't support macros so every Lisp programmer can create their own variants of standard control constructs any more than C supports functions so every C programmer can write trivial variants of the functions in the C standard library. Macros are part of the language to allow you to create abstractions on top of the core language and standard library that move you closer toward being able to directly express the things you want to express.
Perhaps the biggest barrier to a proper understanding of macros is, ironically, that they're so well integrated into the language. In many ways they seem like just a funny kind of function—they're written in Lisp, they take arguments and return results, and they allow you to abstract away distracting details. Yet despite these many