loop
loop Macro
Syntax:
The “simple” loop form:
loop {compound-form}* → {result}*
The “extended” loop form:
loop [↓name-clause] {↓variable-clause}* {↓main-clause}* → {result}*
name-clause::=named name
variable-clause::=↓with-clause | ↓initial-final | ↓for-as-clause
with-clause::=with var1 [type-spec] [= form1] {and var2 [type-spec] [= form2]}*
main-clause::=↓unconditional | ↓accumulation | ↓conditional | ↓termination-test | ↓initial-final initial-final::=initially {compound-form}+| finally {compound-form}+
unconditional::={do | doing} {compound-form}+| return {form | it}
accumulation::=↓list-accumulation | ↓numeric-accumulation
list-accumulation::={collect | collecting | append | appending | nconc | nconcing} {form | it} [into simple-var]
loopnumeric-accumulation::=*{*count | counting | sum | summing |
maximize | maximizing | minimize | minimizing*} {form |* it}
[into simple-var] [type-spec]
conditional::=*{if | when | unless} form ↓selectable-clause {*and ↓selectable-clause}*
[else *↓selectable-clause {*and ↓selectable-clause}*]
[end]
selectable-clause::=↓unconditional | ↓accumulation | ↓conditional
termination-test::=while form | until form | repeat form | always form | never form | thereis form for-as-clause::=*{for | as} ↓for-as-subclause {*and ↓for-as-subclause}*
for-as-subclause::=↓for-as-arithmetic | ↓for-as-in-list | ↓for-as-on-list | ↓for-as-equals-then | ↓for-as-across | ↓for-as-hash | ↓for-as-package
for-as-arithmetic::=var [type-spec] ↓for-as-arithmetic-subclause
for-as-arithmetic-subclause::=↓arithmetic-up | ↓arithmetic-downto | ↓arithmetic-downfrom arithmetic-up::=[[ {from | upfrom} form1 | {to | upto | below} form2 | by form3 ]]+
arithmetic-downto::=[[ {from form1}1| {{downto | above} form2}1| by form3 ]]
arithmetic-downfrom::=[[ {downfrom form1}1| {to | downto | above} form2 | by form3 ]] for-as-in-list::=var [type-spec] in form1 [by step-fun]
for-as-on-list::=var [type-spec] on form1 [by step-fun]
for-as-equals-then::=var [type-spec] = form1 [then form2]
for-as-across::=var [type-spec] across vector
for-as-hash::=var [type-spec] being {each | the}
{{hash-key | hash-keys} {in | of} hash-table
[using (hash-value other-var)] |
{hash-value | hash-values} {in | of} hash-table
[using (hash-key other-var)]}
loopfor-as-package::=var [type-spec] being {each | the}
*{*symbol | symbols |
present-symbol | present-symbols |
external-symbol | external-symbols*}*
[{in | of} package]
type-spec::=↓simple-type-spec | ↓destructured-type-spec
simple-type-spec::=fixnum | float | t | nil
destructured-type-spec::=of-type d-type-spec
d-type-spec::=type-specifier | (d-type-spec . d-type-spec)
var::=↓d-var-spec
var1::=↓d-var-spec
var2::=↓d-var-spec
other-var::=↓d-var-spec
d-var-spec::=simple-var | nil | (↓d-var-spec . ↓d-var-spec)
Arguments and Values:
compound-form—a compound form.
name—a symbol.
simple-var—a symbol (a variable name).
form, form1, form2, form3—a form.
step-fun—a form that evaluates to a function of one argument.
vector—a form that evaluates to a vector .
hash-table—a form that evaluates to a hash table.
package—a form that evaluates to a package designator .
type-specifier—a type specifier . This might be either an atomic type specifier or a compound type specifier , which introduces some additional complications to proper parsing in the face of destructuring; for further information, see Section 6.1.1.7 (Destructuring).
result—an object.
loopDescription:
For details, see Section 6.1 (The LOOP Facility).
Examples:
;; An example of the simple form of LOOP.
(defun sqrt-advisor ()
(loop (format t "~&Number: ")
(let ((n (parse-integer (read-line) :junk-allowed t)))
(when (not n) (return))
(format t "~&The square root of ~D is ~D.~%" n (sqrt n)))))
→ SQRT-ADVISOR
(sqrt-advisor)
▷ Number: 5←
▷ The square root of 5 is 2.236068.
▷ Number: 4←
▷ The square root of 4 is 2.
▷ Number: done←
→ NIL
;; An example of the extended form of LOOP.
(defun square-advisor ()
(loop as n = (progn (format t "~&Number: ")
(parse-integer (read-line) :junk-allowed t))
while n
do (format t "~&The square of ~D is ~D.~%" n (\* n n))))
→ SQUARE-ADVISOR
(square-advisor)
▷ Number: 4←
▷ The square of 4 is 16.
▷ Number: 23←
▷ The square of 23 is 529.
▷ Number: done←
→ NIL
;; Another example of the extended form of LOOP.
(loop for n from 1 to 10
when (oddp n)
collect n)
→ (1 3 5 7 9)
See Also:
do, dolist, dotimes, return, go, throw, Section 6.1.1.7 (Destructuring)
Notes:
Except that loop-finish cannot be used within a simple loop form, a simple loop form is related to an extended loop form in the following way:
(loop {compound-form}*) ≡ (loop do {compound-form}*)
Expanded Reference: loop
Looping over a list
(let ((lst (list 5 4 3 "b" "a")))
(loop for el in lst
do (format t "~S~%" el)))
.. 5
.. 4
.. 3
.. "b"
.. "a"
..
=> NIL
Looping and declaring lexical variables with
Note the usage of the word with below. with will be executed once at the beginning of the loop. See below the usage of for which updates the binding on each run of the loop.
(loop for x below 50
with i = 0
when (and (< i 10)
(not (evenp x)))
do (format t "~D~%" x)
(incf i))
.. 1
.. 3
.. 5
.. 7
.. 9
.. 11
.. 13
.. 15
.. 17
.. 19
..
=> NIL
This is similar to the alternative of wrapping the loop form in a let form.
(let ((i 0))
(loop for x below 50
when (and (< i 10)
(not (evenp x)))
do (print x)
(incf i)))
Loop using with keyword to declare variables that depend on each other
The with form will not have the value of any preceeding variable bound with for at the beginning of the loop. However variables bound with another with will be available.
(loop for x in (list 1 2 3)
with a = 1
with b = (* 8 a)
do (format t "~D~%" b))
.. 8
.. 8
.. 8
..
=> NIL
However using the x variable would not work:
(loop for x in (list 1 2 3)
with a = 1
with b = (* 8 x)
do (print b))
Value of X in (* 8 X) is NIL, not a NUMBER.
[Condition of type SIMPLE-TYPE-ERROR]
Note: The preferred style when using both the with and for keywords is to write the with clauses first:
(loop with a = 2
for y in (list 1 2 3)
do (print y))
Loop with lexical variables and updates
Notice the usage of the for keyword which indicates the binding should be updated on each run of the loop.
(loop for x in (list 1 2 3 4)
for y = (* x 2)
do (format t "~D~%" y))
.. 2
.. 4
.. 6
.. 8
..
=> NIL
The y variable will be updated on each run based on the given form (* x 2)
Looping over a Hash Table
(let ((ht (make-hash-table :test #'equal)))
(setf (gethash "a" ht) 1
(gethash "b" ht) 2)
(loop for key being each hash-key of ht
using (hash-value value)
do (format t "~A: ~A~%" key value)))
(let ((ht (make-hash-table :test #'equal)))
(setf (gethash "a" ht) 1
(gethash "b" ht) 2)
(loop for value being each hash-value of ht
do (format t "~A~%" value)))
(let ((ht (make-hash-table :test #'equal)))
(setf (gethash "a" ht) 1
(gethash "b" ht) 2)
(loop for key being each hash-key of ht
do (format t "~A~%" key)))
Collecting values
The collect clause accumulates results into a list and returns it.
(loop for i from 1 to 5
collect (* i i))
=> (1 4 9 16 25)
Collecting with a filter
(loop for x in '(1 "a" 2 "b" 3 "c")
when (stringp x)
collect x)
=> ("a" "b" "c")
Counting, summing, maximizing, minimizing
(loop for x in '(3 1 4 1 5 9 2 6)
count (evenp x))
=> 3
(loop for x in '(1 2 3 4 5)
sum x)
=> 15
(loop for x in '(3 1 4 1 5 9 2 6)
maximize x)
=> 9
(loop for x in '(3 1 4 1 5 9 2 6)
minimize x)
=> 1
Multiple accumulations with named variables
Using into to accumulate into named variables allows multiple accumulations in a single loop.
(loop for x in '(1 -2 3 -4 5)
when (plusp x) collect x into positives
when (minusp x) collect x into negatives
finally (return (list positives negatives)))
=> ((1 3 5) (-2 -4))
Iterating over a range of numbers
;; from/to is inclusive
(loop for i from 0 to 4 collect i)
=> (0 1 2 3 4)
;; below is exclusive (same as "from 0 to n-1")
(loop for i below 4 collect i)
=> (0 1 2 3)
;; counting downward
(loop for i from 10 downto 1 collect i)
=> (10 9 8 7 6 5 4 3 2 1)
;; with a step
(loop for i from 0 to 20 by 5 collect i)
=> (0 5 10 15 20)
Iterating over a string
(loop for c across "hello"
collect (char-upcase c))
=> (#\H #\E #\L #\L #\O)
Destructuring in for clauses
(loop for (name . age) in '(("Alice" . 30) ("Bob" . 25) ("Carol" . 35))
when (> age 28)
collect name)
=> ("Alice" "Carol")
Iterating with multiple for clauses (parallel termination)
When multiple for clauses are used, the loop terminates when any of them is exhausted.
(loop for x in '(a b c d e)
for i from 1
collect (list i x))
=> ((1 A) (2 B) (3 C) (4 D) (5 E))
;; Terminates when the shorter list runs out
(loop for x in '(a b c)
for y in '(1 2 3 4 5)
collect (list x y))
=> ((A 1) (B 2) (C 3))
Using finally for cleanup or post-processing
(loop for x in '(1 2 3 4 5)
sum x into total
count t into n
finally (return (/ total n)))
=> 3
Conditional execution with if/else
(loop for x in '(1 2 3 4 5 6)
if (evenp x)
collect x into evens
else
collect x into odds
end
finally (return (values evens odds)))
=> (2 4 6)
=> (1 3 5)
Simple (non-extended) loop
Without any loop keywords, loop creates an infinite loop. Use return to exit.
(let ((i 0))
(loop
(when (> i 4) (return i))
(incf i)))
=> 5
Looping across multiple lists simultaneously
(loop for x in '(1 2 3)
for y in '(10 20 30)
collect (+ x y))
=> (11 22 33)
Repeat a fixed number of times
(loop repeat 3
do (format t "hello~%"))
.. hello
.. hello
.. hello
..
=> NIL
(loop repeat 4 collect (random 100))
;; result varies, e.g. (42 17 83 5)
Using thereis, always, and never
;; thereis returns the first non-nil value
(loop for x in '(nil nil 42 nil)
thereis x)
=> 42
;; always returns T if every test passes, NIL otherwise
(loop for x in '(2 4 6 8)
always (evenp x))
=> T
;; never returns T if no element satisfies the test
(loop for x in '(1 3 5 7)
never (evenp x))
=> T