Common Lisp the Language, 2nd Edition

Next: Value Accumulation Up: Loop Previous: Iteration Control

# 26.7. End-Test 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