Next: , Up: Macros for writing loops


6.3.1 Main looping macros

— syntax: iterate
          (iterate loop-name ((seq-type elt-var arg ...)
                              ...)
              ((state-var init)
               ...)
            body
            [tail-exp])

Iterate steps the elt-vars in parallel through the sequences, while each state-var has the corresponding init for the first iteration and later values supplied by the body. If any sequence has reached the limit, the value of the iterate expression is the value of tail-exp, if present, or the current values of the state-vars, returned as multiple values. If no sequence has reached its limit, body is evaluated and either calls loop-name with new values for the state-vars or returns some other value(s).

The loop-name and the state-vars & inits behave exactly as in named-let, in that loop-name is bound only in the scope of body, and each init is evaluated parallel in the enclosing scope of the whole expression. Also, the arguments to the sequence constructors will be evaluated in the enclosing scope of the whole expression, or in an extension of that scope peculiar to the sequence type. The named-let expression

          (let loop-name ((state-var init) ...)
            body
            ...)

is equivalent to an iterate expression with no sequences (and with an explicit let wrapped around the body expressions to take care of any internal definitions):

          (iterate loop-name ()
              ((state-var init) ...)
            (let () body ...))

The seq-types are keywords (actually, macros of a particular form, which makes it easy to add additional types of sequences; see below). Examples are list*, which walks down the elements of a list, and vector*, which does the same for vectors. For each iteration, each elt-var is bound to the next element of the sequence. The args are supplied to the sequence processors as other inputs, such as the list or vector to walk down.

If there is a tail-exp, it is evaluated when the end of one or more sequences is reached. If the body does not call loop-name, however, the tail-exp is not evaluated. Unlike named-let, the behaviour of a non-tail-recursive call to loop-name is unspecified, because iterating down a sequence may involve side effects, such as reading characters from a port.

— syntax: reduce
          (reduce ((seq-type elt-var arg ...)
                   ...)
              ((state-var init)
               ...)
            body
            [tail-exp])

If an iterate expression is not meant to terminate before a sequence has reached its end, the body will always end with a tail call to loop-name. Reduce is a convenient macro that makes this common case explicit. The syntax of reduce is the same as that of iterate, except that there is no loop-name, and the body updates the state variables by returning multiple values in the stead of passing the new values to loop-name: the body must return as many values as there are state variables. By special dispension, if there are no state variables, then the body may return any number of values, all of which are ignored.

The value(s) returned by an instance of reduce is (are) the value(s) returned by the tail-exp, if present, or the current value(s) of the state variables when the end of one or more sequences is reached.

A reduce expression can be rewritten as an equivalent iterate expression by adding a loop-name and a wrapper for the body that calls the loop-name:

          (iterate loop ((seq-type elt-var arg ...)
                         ...)
              ((state-var init)
               ...)
            (call-with-values (lambda () body)
              loop)
            [tail-exp])