Most looping and conditional constructs in Common Lisp are actually macros that hide away more basic constructs. For example, dotimes
and dolist
are built upon the do
macro. The form for do
looks like this:
(do (varlist)
(endlist)
&body)
varlist
is composed of the variables defined in the loop, their
initial values, and how they change after each iteration. The 'change' portion is evaluated at the end of the loop.endlist
contains the end conditions and the values returned at the end of the loop. The end condition is evaluated at the beginning of the loop.Here's one that starts at 0 and goes upto (not including) 10.
;;same as (dotimes (i 10))
(do (( i (+ 1 i))
((< i 10) i)
(print i))
And here's one that moves through a list:
;;same as (dolist (item given-list)
(do ((item (car given-list))
(temp list (cdr temp))
(print item))
The varlist
portion is similar the one in a let
statement. You can bind more than one variable, and they only exist inside the loop. Each variable declared is in its own set of parenthesis. Here's one that counts how many 1's and 2's are in a list.
(let ((vars (list 1 2 3 2 2 1)))
(do ((ones 0)
(twos 0)
(temp vars (cdr temp)))
((not temp) (list ones twos))
(when (= (car temp) 1)
(setf ones (+ 1 ones)))
(when (= (car temp) 2)
(setf twos (+ 1 twos)))))
-> (2 3)
And if a while loop macro hasn't been implemented:
(do ()
(t)
(when task-done
(break)))
For the most common applications, the more specific dotimes
and doloop
macros are much more succinct.