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 (print 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 (print 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 (print 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 (print 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 ((given-ht (serapeum:dict "a" 1 "b" 2)))
(loop for key being each hash-key of given-ht
using (hash-value value)
do (format t "~A: ~A ~%" key value)))

a: 1
b: 2
NIL

(let ((given-ht (serapeum:dict "a" 1 "b" 2)))
(loop for value being each hash-value of given-ht
do (format t "~A~%" value)))

1
2
NIL

(let ((given-ht (serapeum:dict "a" 1 "b" 2)))
(loop for key being each hash-key of given-ht
do (format t "~A~%" key)))

a
b
NIL