Next: Sequence types, Up: Macros for writing loops
(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 theiterate
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, andvector*
, 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.
(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 ofreduce
is the same as that ofiterate
, 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 equivalentiterate
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])