WhizzML Reference Manual
2.9 Iteration
Iteration is accomplished explicitly using the loop and recur keywords, or implicitly using either map or for , as well as the standard library procedure reduce and the special syntax iterate (see subsection 4.7.7 for details on the latter two).
2.9.1 Iteration with loop/recur
Generic iteration can be attained using the loop-recur form, which has the structure:
(loop (<id0> <val0> ... <idn> <valn>) <body>)
<id0> ... <idn> := identifiers bound as variables in <body> with
values <val0> ... <valn> in the first iteration
<body> := an arbitrary body that can contain calls of the form
(recur <val0'> ... <valn'>), which cause the loop body
to be re-entered with <id0> ... <idn> bound to the new
values <val0'> ... <valn'>
For instance, this loop reverses the list [1 2 3 4]:
(loop (in [1 2 3 4]
out [])
(if (= [] in)
out ;; we're done iterating, return the result
(recur (tail in) ;; we iterate with in -> (tail in)
(cons (head in) out)) ;; and out -> (cons (head in) out)
and in this other example we compute the number of even values in an input list:
(loop (in input-list
cnt 0)
(cond (= [] input-list) cnt
(= 0 (rem (head input-list) 2)) (recur (tail input-list) (+ 1 cnt))
(recur (tail input-list) cnt)))
The special syntactic form iterate and the standard procedure reduce, which are fully described in subsection 4.7.7 , simplify writing loops that compute a final value by traversing several lists.
2.9.2 List value mapping with map
Although it could be defined as a user procedure, WhizzML provides a built-in map that applies a given procedure (its first argument) to each of the elements of the lists provided as a second and subsequent arguments, and returns the list of results.
(map <proc> <list0> <list1> ...)
For example:
(map (lambda (x) (+ x 1)) [2 4 6]) ;; => [3 5 7]
(map (lambda (x y) (+ x y)) [1 2 3] [2 3 4]) ;; => [3 5 7]
(map list [true false] [1 2] ["a" "b"]) ;; => [[true 1 "a"] [false 2 "b"]]
For a single list, map is functionally equivalent to the following naive definition:
(define (map fn lst)
(cond (empty? lst) lst
(cons (fn (head lst)) (map fn (tail lst)))))
For multiple lists, <proc> must take as many arguments as lists are passed in the call, and the mapping stops when the shortest of the given lists is exhausted. For example:
(map (lambda (x y z) (+ x y z)) (range 10) (range 20) (range 2)) ;; => [0 3]
(map (lambda (x y) (if (> x y) x y)) [1 2 3 4] [-1 5]) ;; => [1 5]
Despite its being a special form, map can be used as a procedural value, i.e., as an argument to other higher order functions, but it’s worth noting that in those cases the performance of the multiple-lists version will be noticeably worse than the built in invoked when map is used in call position.
2.9.3 List value mapping with for
The for form is an alternative form of expressing mapping over lists where we use a body for the values to be computed in terms of a variable that iterates over a given list.
(for (<id> <list>) <body>)
which is equivalent to:
(map (lambda (<id>) <body>) <list>)
Examples:
(for (x [1 2 3]) (+ x 1)) ;; => [2 3 4]
(for (x (range 0 10))
(if (= 0 (rem x 3)) x 0)) ;; => [0 0 0 3 0 0 6 0 0 9]