Skip to main content

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]

loop

numeric-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)]}

loop

for-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.

loop

Description:

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