Scheme has only few iteration mechanisms, mainly because iteration in
Scheme programs is normally expressed using recursion. Nevertheless,
R5RS defines a construct for programming loops, calling
addition, Guile has an explicit looping syntax called
Bind variables and evaluate body until test is true. The return value is the last expr after test, if given. A simple example will illustrate the basic form,
(do ((i 1 (1+ i))) ((> i 4)) (display i)) -| 1234
Or with two variables and a final return value,
(do ((i 1 (1+ i)) (p 3 (* 3 p))) ((> i 4) p) (format #t "3**~s is ~s\n" i p)) -| 3**1 is 3 3**2 is 9 3**3 is 27 3**4 is 81 ⇒ 789
The variable bindings are established like a
let, in that
the expressions are all evaluated and then all bindings made. When
iterating, the optional step expressions are evaluated with the
previous bindings in scope, then new bindings all made.
The test expression is a termination condition. Looping stops when the test is true. It’s evaluated before running the body each time, so if it’s true the first time then body is not run at all.
The optional exprs after the test are evaluated at the end of looping, with the final variable bindings available. The last expr gives the return value, or if there are no exprs the return value is unspecified.
Each iteration establishes bindings to fresh locations for the
variables, like a new
let for each iteration. This is
done for variables without step expressions too. The
following illustrates this, showing how a new
i is captured by
lambda in each iteration (see The
Concept of Closure).
(define lst '()) (do ((i 1 (1+ i))) ((> i 4)) (set! lst (cons (lambda () i) lst))) (map (lambda (proc) (proc)) lst) ⇒ (4 3 2 1)
Run a loop executing the body forms while cond is true.
cond is tested at the start of each iteration, so if it’s
#f the first time then body is not executed at all.
while, two extra bindings are provided, they can be used
from both cond and body.
Break out of the
Abandon the current iteration, go back to the start and test cond again, etc.
If the loop terminates normally, by the cond evaluating to
#f, then the
while expression as a whole evaluates to
#f. If it terminates by a call to
break with some number
of arguments, those arguments are returned from the
expression, as multiple values. Otherwise if it terminates by a call to
break with no arguments, then return value is
(while #f (error "not reached")) ⇒ #f (while #t (break)) ⇒ #t (while #t (break 1 2 3)) ⇒ 1 2 3
while form gets its own
procedures, operating on that
while. This means when loops are
nested the outer
break can be used to escape all the way out.
(while (test1) (let ((outer-break break)) (while (test2) (if (something) (outer-break #f)) ...)))
Note that each
continue procedure can only be
used within the dynamic extent of its
while. Outside the
while their behaviour is unspecified.
Another very common way of expressing iteration in Scheme programs is the use of the so-called named let.
Named let is a variant of
let which creates a procedure and calls
it in one step. Because of the newly created procedure, named let is
more powerful than
do–it can be used for iteration, but also
for arbitrary recursion.
For the definition of bindings see the documentation about
let (see Local Bindings).
let works as follows:
let) to variable. The new procedure’s formal argument names are the name of the variables.
The next example implements a loop which iterates (by recursion) 1000 times.
(let lp ((x 1000)) (if (positive? x) (lp (- x 1)) x)) ⇒ 0