The do
macro is the fundamental iteration operator in Common Lisp. Like
let
, do
can create variables, and the first argument is a list of variable
specifications. Each element of this list can be of the form:
(variable initial update)
where variable is a symbol, and initial and update are expressions.
Initially, each variable will be set to the value of the corresponding
initial; on each iteration it will be set to the value of the corresponding
update. The do
in show-squares
creates just one variable, i
. On the
first iteration, i
will be set to the value of start
, and on successive
iterations its value will be incremented by one.
The second argument to do
should be a list containing one or more
expressions. The first expression is used to test whether iteration should
stop. In the cae above, the test expression is (> i end)
. The remaining
expressions in this list will be evaluated in order when iteration stops, and
the value of the last will be returned as the value of the do
.
The remaining arguments to do
comprise the body of the loop. They will be
evaluated, in order, on each iteration. On each iteration, the variables are
updated, then the termination test is evaluated, and then (if the test failed)
the body is evaluated.
For comparison, here is a recursive version of show-squares
:
(defun show-squares (i end) (if (> i end) 'done (progn (format t "~A ~A~%" i (( i i))) (show-squares (+ i 1) end))))
The only thing new in this function is progn
. It takes any number of
expressions, evaluates them in order, and returns the value of the last.
Common Lisp has simpler iteration operators for special cases. To iterate
through the elements of a list, for example, you would use dolist
. Here is a
function that returns the length of list:
(defun our-length (lst) (let ((len 0)) (dolist (obj lst) (setf len (+ len 1))) len))
Here, dolist
takes an argument of the form ‘(variable expression)’, followed
by a body of expressions. The body will be evaluated with variable bound to
successive elements of the list returned by expression. The obvious
recursive version of this function would be:
(defun our-length (lst) (if (null lst) 0 (+ (our-length (cdr lst)) 1)))
If the list is empty, its length is zero; otherwise it is the length of the cdr
plus one. This version of our-length
is cleaner, but because it’s not
tail-recursive, it won’t be as efficient.