the branches appear in the body until one of them evaluates to true. At that point, the remaining forms in that branch are evaluated, and the value of the last form in the branch is returned as the value of the COND
as a whole. If the branch contains no forms after the condition, the value of the condition is returned instead. By convention, the branch representing the final else clause in an if/else-if chain is written with a condition of T
. Any non- NIL
value will work, but a T
serves as a useful landmark when reading the code. Thus, you can write the previous nested IF
expression using COND
like this:
(cond (a (do-x))
(b (do-y))
(t (do-z)))
When writing the conditions in IF
, WHEN
, UNLESS
, and COND
forms, three operators that will come in handy are the boolean logic operators, AND
, OR
, and NOT
.
NOT
is a function so strictly speaking doesn't belong in this chapter, but it's closely tied to AND
and OR
. It takes a single argument and inverts its truth value, returning T
if the argument is NIL
and NIL
otherwise.
AND
and OR
, however, are macros. They implement logical conjunction and disjunction of any number of subforms and are defined as macros so they can AND
stops and returns NIL
as soon as one of its subforms evaluates to NIL
. If all the subforms evaluate to non- NIL
, it returns the value of the last subform. OR
, on the other hand, stops as soon as one of its subforms evaluates to non-NIL
and returns the resulting value. If none of the subforms evaluate to true, OR
returns NIL
. Here are some examples:
(not nil) ==> T
(not (= 1 1)) ==> NIL
(and (= 1 2) (= 3 3)) ==> NIL
(or (= 1 2) (= 3 3)) ==> T
Control constructs are the other main kind of looping constructs. Common Lisp's looping facilities are—in addition to being quite powerful and flexible—an interesting lesson in the have-your-cake-and-eat-it-too style of programming that macros provide.
As it turns out, none of Lisp's 25 special operators directly support structured looping. All of Lisp's looping control constructs are macros built on top of a pair of special operators that provide a primitive goto facility.[88] Like many good abstractions, syntactic or otherwise, Lisp's looping macros are built as a set of layered abstractions starting from the base provided by those two special operators.
At the bottom (leaving aside the special operators) is a very general looping construct, DO
. While very powerful, DO
suffers, as do many general-purpose abstractions, from being overkill for simple situations. So Lisp also provides two other macros, DOLIST
and DOTIMES
, that are less flexible than DO
but provide convenient support for the common cases of looping over the elements of a list and counting loops. While an implementation can implement these macros however it wants, they're typically implemented as macros that expand into an equivalent DO
loop. Thus, DO
provides a basic structured looping construct on top of the underlying primitives provided by Common Lisp's special operators, and DOLIST
and DOTIMES
provide two easier-to-use, if less general, constructs. And, as you'll see in the next chapter, you can build your own looping constructs on top of DO
for situations where DOLIST
and DOTIMES
don't meet your needs.
Finally, the LOOP
macro provides a full-blown mini-language for expressing looping constructs in a non-Lispy, English-like (or at least Algol-like) language. Some Lisp hackers love LOOP
; others hate it. LOOP
's fans like it because it provides a concise way to express certain commonly needed looping constructs. Its detractors dislike it because it's not Lispy enough. But whichever side one comes down on, it's a remarkable example of the power of macros to add new constructs to the language.
I'll start with the easy-to-use DOLIST
and DOTIMES
macros.
DOLIST
loops across the items of a list, executing the loop body with a variable holding the successive items of the list.[89] This is the basic skeleton (leaving out some of the more esoteric options):
(dolist (
When the loop starts, the
CL-USER> (dolist (x '(1 2 3)) (print x))
1
2
3
NIL
Used this way, the DOLIST
form as a whole evaluates to NIL
.
If you want to break out of a DOLIST
loop before the end of the list, you can use RETURN
.
CL-USER> (dolist (x '(1 2 3)) (print x) (if (evenp x) (return)))
1
2