For Development HEAD DRAFTSearch (procedure/syntax/module):

4.8 Iteration

Special Form: do ((variable init [step]) …) (test expr …) body …

[R7RS base]

  1. Evaluates init … and binds variable … to each result. The following steps are evaluated under the environment where variables are bound.
  2. Evaluate test. If it yields true, evaluates expr … and returns the result(s) of last expr.
  3. Otherwise, evaluates body … for side effects.
  4. Then evaluates step … and binds each result to a fresh variable …, and repeat from the step 2.

The following example loops 10 times while accumulating each value of i to j and returns it.

(do ((i 0 (+ i 1))
     (j 0 (+ i j)))
    ((= i 10) j)
  (print j))
 ⇒ 45 ; also prints intermediate values of j

If step is omitted, the previous value of variable is carried over. When there’s no expr, the non-false value returned by test becomes the value of the do expression.

Since do syntax uses many parentheses, some prefer using square brackets as well as parentheses to visually distinguish the groupings. A common way is to group each variable binding, and the test clause, by square brackets.

(do ([i 0 (+ i 1)]
     [j 0 (+ i j)])
    [(= i 10) j]
  (print j))

Note: Unlike Common Lisp (and “for loops” in many languages), variable is freshly bound for each iteration. The following example loops 5 times and creates a list of closures, each of which closes the variable i. When you call each closures, you can see that each of them closes different i at the time of the iteration they were created.

(define closures
  (do ([i 0 (+ i 1)]
       [c '() (cons (^[] i) c)])
      [(= i 5) (reverse c)]
    ))

((car closures))  ⇒ 0
((cadr closures)) ⇒ 1
Special Form: let name ((var init) …) body …

[R7RS base] This variation of let is called “named let”. It creates the following procedure and binds it to name, then calls it with init ….

(lambda (var ...) body ...)

This syntax itself isn’t necessarily related to iteration. However, the whole point of named let is that the above lambda expression is within the scope of name—that is, you can call name recursively within body. Hence this is used very often to write a loop by recursion (thus, often the procedure is named loop, as in the following example.)

(let loop ([x 0] [y '()])
  (if (= x 10)
    y
    (loop (+ x 1) (cons x y))))
 ⇒ (9 8 7 6 5 4 3 2 1 0)

Of course you don’t need to loop with a named let; you can call name in non-tail position, pass name to other higher-order procedure, etc. Named let exists since it captures a very common pattern of local recursive procedures. Some Schemers even prefer named let to do, for the better flexibility.

The following rewrite rule precisely explains the named let semantics. The tricky use of letrec in the expansion is to make proc visible from body … but not from init ….

(let proc ((var init) ...) body ...)
 ≡
((letrec ((proc (lambda (var ...) body ...)))
   proc)
 init ...)
Macro: dotimes ([variable] num-expr [result]) body …
Macro: dolist ([variable] list-expr [result]) body …

Convenience loop syntaxes, imported from Common Lisp. They are not very Scheme-y, in a sense that these rely on some side-effects in body …. Nevertheless these capture some common pattern in day-to-day scripting.

You can use dotimes to repeat body … for a number of times given by num-expr, and dolist to repeat body … while traversing a list given by list-expr. While body … is evaluated, variable is bound to the current iteration count (in dotimes), or the current element in the list (in dolist).

(dotimes (n 5) (write n))
 ⇒ writes "01234"

(dolist (v '(a b c d e)) (write v))
 ⇒ writes "abcde"

If you don’t need to refer to variable, you can omit it. For example, the following example prints year! 10 times:

(dotimes (10) (print "yeah!"))

If the third element (result) is given in dotimes or dolist, it is evaluated after all repetition is done, and its result becomes the result of dotimes/dolist. While result is evaluated, variable is bound to the number of repetitions (in dotimes) or () (in dolist). It is supported because Common Lisp has it.

Note that a fresh variable is bound for each iteration, as opposed to Common Lisp where variable is mutated. So if you create a closure closing variable, it won’t be overwritten by the subsequent iteration.

If you need more than simple iteration, you can use do form, named let, or srfi.42 - Eager comprehensions, which provides rich way to iterate.

Macro: while expr body …
Macro: while expr => var body …
Macro: while expr guard => var body …

Var is an identifier and guard is a procedure that takes one argument.

In the first form, expr is evaluated, and if it yields a true value, body … are evaluated. It is repeated while expr yields true value.

In the second form, var is bound to a result of expr in the scope of body ….

In the third form, the value expr yields are passed to guard, and the execution of body … is repeated while guard returns a true value. var is bound to the result of expr.

The return value of while form itself isn’t specified.

(let ((a '(0 1 2 3 4)))
  (while (pair? a)
    (write (pop! a)))) ⇒ prints "01234"

(let ((a '(0 1 2 3 #f 5 6)))
  (while (pop! a) integer? => var
    (write var))) ⇒ prints "0123"
Macro: until expr body …
Macro: until expr guard => var body …

Like while, but the condition is reversed. That is, the first form repeats evaluation of expr and body … until expr yields true. In the second form, the result of expr is passed to guard, and the execution is repeated until it returns true. Var is bound to the result of expr.

(The second form without guard isn’t useful in until, since var would always be bound to #f).

The return value of until form itself isn’t specified.

(let ((a '(0 1 2 3 4)))
  (until (null? a)
    (write (pop! a)))) ⇒ prints "01234"

(until (read-char) eof-object? => ch
  (write-char ch))
 ⇒ reads from stdin and writes char until EOF is read


For Development HEAD DRAFTSearch (procedure/syntax/module):
DRAFT