Common Lisp the Language, 2nd Edition


next up previous contents index
Next: Value Accumulation Up: Loop Previous: Iteration Control

26.7. End-Test Control

change_begin
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


change_end



next up previous contents index
Next: Value Accumulation Up: Loop Previous: Iteration Control


AI.Repository@cs.cmu.edu