Common Lisp the Language, 2nd Edition
Next: Value Accumulation
Up: Loop
Previous: Iteration
Control
The loop keywords always
, never
,
thereis
, until
, and while
designate constructs that use a single test condition to determine when
loop iteration should terminate.
The constructs always
, never
, and
thereis
provide specific values to be returned when a loop
terminates. Using always
, never
, or
thereis
with value-returning accumulation clauses can
produce unpredictable results. In all other respects these constructs
behave like the while
and until
constructs.
The macro loop-finish
can be used at any time to cause
normal termination. In normal termination, finally
clauses
are executed and default return values are returned.
End-test control constructs can be used anywhere within the loop body. The termination conditions are tested in the order in which they appear.
[Loop Clause]
while
expr
until
expr
The while
construct allows iteration to continue until
the specified expression expr evaluates to nil
.
The expression is re-evaluated at the location of the while
clause.
The until
construct is equivalent to while
(not
expr). If the value of the specified
expression is non-nil
, iteration terminates.
You can use while
and until
at any point in
a loop. If a while
or until
clause causes
termination, any clauses that precede it in the source are still
evaluated.
Examples:
;;; A classic "while-loop".
(loop while (hungry-p) do (eat))
;;; UNTIL NOT is equivalent to WHILE.
(loop until (not (hungry-p)) do (eat))
;;; Collect the length and the items of STACK.
(let ((stack '(a b c d e f)))
(loop while stack
for item = (length stack) then (pop stack)
collect item))
=> (6 A B C D E F)
;;; Use WHILE to terminate a loop that otherwise wouldn't
;;; terminate. Note that WHILE occurs after the WHEN.
(loop for i fixnum from 3
when (oddp i) collect i
while (< i 5))
=> (3 5)
[Loop Clause]
always
expr
never
expr
thereis
expr
The always
construct takes one form and terminates the
loop if the form ever evaluates to nil
; in this case, it
returns nil
. Otherwise, it provides a default return value
of t
.
The never
construct takes one form and terminates the
loop if the form ever evaluates to non-nil
; in this case,
it returns nil
. Otherwise, it provides a default return
value of t
.
The thereis
construct takes one form and terminates the
loop if the form ever evaluates to non-nil
; in this case,
it returns that value.
If the while
or until
construct causes
termination, control is passed to the loop epilogue, where any
finally
clauses will be executed. Since
always
, never
, and thereis
use
the Common Lisp macro return
to terminate iteration, any
finally
clause that is specified is not evaluated.
Examples:
;;; Make sure I is always less than 11 (two ways).
;;; The FOR construct terminates these loops.
(loop for i from 0 to 10
always (< i 11))
=> T
(loop for i from 0 to 10
never (> i 11))
=> T
;;; If I exceeds 10, return I; otherwise, return NIL.
;;; The THEREIS construct terminates this loop.
(loop for i from 0
thereis (when (> i 10) i) )
=> 11
;;; The FINALLY clause is not evaluated in these examples.
(loop for i from 0 to 10
always (< i 9)
finally (print "you won't see this"))
=> NIL
(loop never t
finally (print "you won't see this"))
=> NIL
(loop thereis "Here is my value"
finally (print "you won't see this"))
=> "Here is my value"
;;; The FOR construct terminates this loop,
;;; so the FINALLY clause is evaluated.
(loop for i from 1 to 10
thereis (> i 11)
finally (print i)) `;Prints 1 line
11
=> NIL
(defstruct mountain height difficulty (why "because it is there"))
(setq everest (make-mountain :height '(2.86e-13 parsecs)))
(setq chocorua (make-mountain :height '(1059180001 microns)))
(defstruct desert area (humidity 0))
(setq sahara (make-desert :area '(212480000 square furlongs)))
`;First there is a mountain, then there is no mountain, then there is ...
(loop for x in (list everest sahara chocorua) `; -GLS
thereis (and (mountain-p x) (mountain-height x)))
=> (2.86E-13 PARSECS)
;;; If you could use this code to find a counterexample to
;;; Fermat's last theorem, it would still not return the value
;;; of the counterexample because all of the THEREIS clauses
;;; in this example return only T. Of course, this code has
;;; never been observed to terminate.
(loop for z upfrom 2
thereis
(loop for n upfrom 3 below (log z 2)
thereis
(loop for x below z
thereis
(loop for y below z
thereis (= (+ (expt x n)
(expt y n))
(expt z n))))))
[Macro]
loop-finish
The macro loop-finish
terminates iteration normally and
returns any accumulated result. If specified, a finally
clause is evaluated.
In most cases it is not necessary to use loop-finish
because other loop control clauses terminate the loop. Use
loop-finish
to provide a normal exit from a nested
condition inside a loop.
You can use loop-finish
inside nested Lisp code to
provide a normal exit from a loop. Since loop-finish
transfers control to the loop epilogue, using loop-finish
within a finally
expression can cause infinite looping.
Implementations are allowed to provide this construct as a local
macro by using macrolet
.
Examples:
;;; Print a date in February, but exclude leap day.
;;; LOOP-FINISH exits from the nested condition.
(loop for date in date-list
do (case date
(29 (when (eq month 'february)
(loop-finish))
(format t "~:@(~A~) ~A" month date))))
;;; Terminate the loop, but return the accumulated count.
(loop for i in '(1 2 3 stop-here 4 5 6)
when (symbolp i) do (loop-finish)
count i)
=> 3
;;; This loop works just as well as the previous example.
(loop for i in '(1 2 3 stop-here 4 5 6)
until (symbolp i)
count i)
=> 3
Next: Value Accumulation
Up: Loop
Previous: Iteration
Control
AI.Repository@cs.cmu.edu