c i r r u s  m a c h i n a

Mobile, Distributed and Cloud Computing Application Development

Information Architecture

Application Usability Design

The Clojure For Macro

I've been playing with the for macro. Its syntax is the same as the syntax of doseq and allows, erm, for arbitrary bound forms of sequences, conditions and arbitrary body expressions derived from the values returned as a sequence.

The following creates a bound d sequence as a list of the values 0 to 5 and f as a bound sequence as a list of the values 5 through 10.

(for [d (range 5) f (range 5 10)] [d f])

This outputs the combinations of the two sequences, as such:

([0 5] [0 6] [0 7] [0 8] [0 9] [1 5] [1 6] [1 7] [1 8] [1 9] [2 5] [2 6] [2 7] [2 8] [2 9] [3 5] [3 6] [3 7] [3 8] [3 9] [4 5] [4 6] [4 7] [4 8] [4 9])

Don't think of them as just data because d is an outer loop and f is an inner loop. This shows especially when you use the condition keywords, as follows:

The :when keyword is handled specially as a filter / condition on anything, including tests on each element the materialized bound sequence values declared earlier. For example:

(for [d (range 5) f (range 5 10) :when (and (odd? d) (odd? f))] [d f])

([1 5] [1 7] [1 9] [3 5] [3 7] [3 9])

You can use the keywords :while, :let and :when for special handling in the outbound sequence:

:when will expand to an if special form with the subsequent body :while will execute to a while condition test, with an implicit do with the subsequent body :let will create a new scope with the subsequent bound form

The difference between :when and :while often work out to the same, but is revealed starkly when you try something like this:

(for [x (range 5) y (shuffle (range 5)) :when (< y 3)] [x y])

([0 0] [0 2] [0 1] [1 0] [1 1] [1 2] [2 2] [2 0] [2 1] [3 2] [3 1] [3 0] [4 0] [4 2] [4 1])

The result will always contain the same values (although in different order). But when you change it to :while, you'll see what ends up in the results changes on each invocation.

(for [x (range 5) y (shuffle (range 5)) :while (< y 3)] [x y])

([0 2] [0 0] [3 1] [3 0] [4 1])

This is because :while will terminate the loop at the first value in y that's less than three, where :when will continue, only skipping those that do not fit the condition, until at least one of the sequences are exhausted.

tags: clojure programming lisp macros
by: Nick Bauman when: 3 years, 6 months ago
Comments
 

More...