Putting It All Together

Now you’ve seen all the main features of the **LOOP** facility. You can combine any of the clauses I’ve discussed as long as you abide by the following rules:

  • The named clause, if any, must be the first clause.
  • After the named clause come all the initially, with, for, and repeat clauses.
  • Then comes the body clauses: conditional and unconditional execution, accumulation, and termination test.11
  • End with any finally clauses.

The **LOOP** macro will expand into code that performs the following actions:

  • Initializes all local loop variables as declared with with or for clauses as well as those implicitly created by accumulation clauses. The initial value forms are evaluated in the order the clauses appear in the loop.
  • Execute the forms provided by any initially clauses—the prologue—in the order they appear in the loop.
  • Iterate, executing the body of the loop as described in the next paragraph.
  • Execute the forms provided by any finally clauses—the epilogue—in the order they appear in the loop.

While the loop is iterating, the body is executed by first stepping any iteration control variables and then executing any conditional or unconditional execution, accumulation, or termination test clauses in the order they appear in the loop code. If any of the clauses in the loop body terminate the loop, the rest of the body is skipped and the loop returns, possibly after running the epilogue.

And that’s pretty much all there is to it.12 You’ll use **LOOP** fairly often in the code later in this book, so it’s worth having some knowledge of it. Beyond that, it’s up to you how much you use it.

And with that, you’re ready to dive into the practical chapters that make up the rest of the book—up first, writing a spam filter.


1The term loop keyword is a bit unfortunate, as loop keywords aren’t keywords in the normal sense of being symbols in the KEYWORD package. In fact, any symbol, from any package, with the appropriate name will do; the **LOOP** macro cares only about their names. Typically, though, they’re written with no package qualifier and are thus read (and interned as necessary) in the current package.

2Because one of the goals of **LOOP** is to allow loop expressions to be written with a quasi-English syntax, many of the keywords have synonyms that are treated the same by **LOOP** but allow some freedom to express things in slightly more idiomatic English for different contexts.

3You may wonder why **LOOP** can’t figure out whether it’s looping over a list or a vector without needing different prepositions. This is another consequence of **LOOP** being a macro: the value of the list or vector won’t be known until runtime, but **LOOP**, as a macro, has to generate code at compile time. And **LOOP**‘s designers wanted it to generate extremely efficient code. To be able to generate efficient code for looping across, say, a vector, it needs to know at compile time that the value will be a vector at runtime—thus, the different prepositions are needed.

4Don’t ask me why **LOOP**‘s authors chickened out on the no-parentheses style for the **using** subclause.

5The trick is to keep ahold of the tail of the list and add new cons cells by **SETF**ing the **CDR** of the tail. A handwritten equivalent of the code generated by (loop for i upto 10 collect i) would look like this:

  1. (do ((list nil) (tail nil) (i 0 (1+ i)))
  2. ((> i 10) list)
  3. (let ((new (cons i nil)))
  4. (if (null list)
  5. (setf list new)
  6. (setf (cdr tail) new))
  7. (setf tail new)))

Of course you’ll rarely, if ever, write code like that. You’ll use either **LOOP** or (if, for some reason, you don’t want to use **LOOP**) the standard **PUSH**/**NREVERSE** idiom for collecting values.

6Recall that **NCONC** is the destructive version of **APPEND**--it’s safe to use an nconc clause only if the values you’re collecting are fresh lists that don’t share any structure with other lists. For instance, this is safe:

  1. (loop for i upto 3 nconc (list i i)) ==> (0 0 1 1 2 2 3 3)

But this will get you into trouble:

  1. (loop for i on (list 1 2 3) nconc i) ==> undefined

The later will most likely get into an infinite loop as the various parts of the list produced by (list 1 2 3) are destructively modified to point to each other. But even that’s not guaranteed—the behavior is simply undefined.

7”No! Try not. Do . . . or do not. There is no try.” — Yoda, The Empire Strikes Back

8I’m not picking on Perl here—this example would look pretty much the same in any language that bases its syntax on C’s.

9Perl would let you get away with not declaring those variables if your program didn’t use strict. But you should always use strict in Perl. The equivalent code in Python, Java, or C would always require the variables to be declared.

10You can cause a loop to finish normally, running the epilogue, from Lisp code executed as part of the loop body with the local macro **LOOP-FINISH**.

11Some Common Lisp implementations will let you get away with mixing body clauses and for clauses, but that’s strictly undefined, and some implementations will reject such loops.

12The one aspect of **LOOP** I haven’t touched on at all is the syntax for declaring the types of loop variables. Of course, I haven’t discussed type declarations outside of **LOOP** either. I’ll cover the general topic a bit in Chapter 32. For information on how they work with **LOOP**, consult your favorite Common Lisp reference.