[ANN] clj-iter, an iteration macro for Clojure inspired by Common Lisp's Iterate

168 views
Skip to first unread message

Daniel Janus

unread,
Nov 5, 2009, 7:03:26 PM11/5/09
to Clojure
Dear all,

I am happy to announce the public availability of clj-iter, an Iterate-
like iteration macro. It is free (available under the terms of MIT
license) and can be found on GitHub: http://github.com/nathell/clj-iter

The design goal was to keep it as simple as possible, and make it
blend well with the rest of Clojure. In
contrast to cl-loop, which uses mutable bindings, clj-iter has a
functional flavour, and macroexpands to the kind of code you would
write manually using loop/recur. It is also very simple, having a
fraction of Iterate's functionality, but I hope even the little there
is will be sufficient in many cases.

To avoid citing the entire README blurb, I'll just give you some
examples:

(iter (for x in [31 41 59 26])
(for y from 1)
(collect (+ x y)))
==> (32 43 62 30)

(iter (for s on [1 2 3 4 5])
(for q initially () then (cons (first s) q))
(collect (cons (first s) (concat (take 2 (rest s)) (take 2
q)))))
==> ((1 2 3) (2 3 4 1) (3 4 5 2 1) (4 5 3 2) (5 4 3))

Please let me know whether it is of any use to you. Feedback, and
especially patches, are more than welcome.

Stefan Arentz

unread,
Nov 5, 2009, 7:33:54 PM11/5/09
to clo...@googlegroups.com

This is awesome. Can this be made part of contrib?

S.

kyle smith

unread,
Nov 5, 2009, 7:58:26 PM11/5/09
to Clojure
What's wrong with (map + [31 41 59 26] (iterate inc 1)) ?

(use 'clojure.contrib.seq-utils)

(defn sliding-window [coll size]
(let [idx (into {} (indexed coll))]
(map #(vals (select-keys idx (range (- % size) (+ % size 1))))
(range (count coll)))))

This is the same as your second example if order isn't important.
Someone else might be able to come up with something better. Do you
have any more/better use cases?

John Harrop

unread,
Nov 5, 2009, 8:02:49 PM11/5/09
to clo...@googlegroups.com
On Thu, Nov 5, 2009 at 7:03 PM, Daniel Janus <nat...@gmail.com> wrote:
To avoid citing the entire README blurb, I'll just give you some
examples:

   (iter (for x in [31 41 59 26])
         (for y from 1)
         (collect (+ x y)))
   ==> (32 43 62 30)

   (iter (for s on [1 2 3 4 5])
       (for q initially () then (cons (first s) q))
       (collect (cons (first s) (concat (take 2 (rest s)) (take 2
q)))))
   ==> ((1 2 3) (2 3 4 1) (3 4 5 2 1) (4 5 3 2) (5 4 3))

I hate to be the party-pooper in this bunch, but what's the advantage over:

(map + [31 41 59 26] (iterate inc 1))

and

(let [s (take-while identity (iterate next [1 2 3 4 5]))]
  (map #(concat (cons (first %1) (concat (take 2 (rest %1)) (take 2 %2))))
    s
    (reductions #(cons (first %2) %1) () s)))

?

Daniel Janus

unread,
Nov 5, 2009, 8:51:35 PM11/5/09
to Clojure
On 6 Lis, 02:02, John Harrop <jharrop...@gmail.com> wrote:
> On Thu, Nov 5, 2009 at 7:03 PM, Daniel Janus <nath...@gmail.com> wrote:
> > To avoid citing the entire README blurb, I'll just give you some
> > examples:
>
> >    (iter (for x in [31 41 59 26])
> >          (for y from 1)
> >          (collect (+ x y)))
> >    ==> (32 43 62 30)
>
> >    (iter (for s on [1 2 3 4 5])
> >        (for q initially () then (cons (first s) q))
> >        (collect (cons (first s) (concat (take 2 (rest s)) (take 2
> > q)))))
> >    ==> ((1 2 3) (2 3 4 1) (3 4 5 2 1) (4 5 3 2) (5 4 3))
>
> I hate to be the party-pooper in this bunch

On the contrary, criticism is most welcome too. Especially when the
comments show a way of solving a problem (as in the case of the
sliding window) I would never have thought of. Thank you (and also to
Kyle for offering up another variant)!

> but what's the advantage over:
>
> (map + [31 41 59 26] (iterate inc 1))
>
> and
>
> (let [s (take-while identity (iterate next [1 2 3 4 5]))]
>   (map #(concat (cons (first %1) (concat (take 2 (rest %1)) (take 2 %2))))
>     s
>     (reductions #(cons (first %2) %1) () s)))

One possible answer is clarity. It's mentally easier for me to parse
the clj-iter version than either alternative versions proposed.
Certainly, it is a matter of personal taste, but then again, you don't
have to use it if you don't want to.

As another example, consider multiplying the first 42 elements of a
list of numbers by 42, and leaving the rest unchanged. It's much more
straightforward for me to write (and then read)

(iter (for x in lst)
(for i from 0)
(collect (if (< i 42) (* x 42) x)))

than something along the lines of

(map (fn [[i x]] (if (< i 42) (* x 42) x))
(map vector (iterate inc 0) lst))

Thanks again,
--Daniel

Timothy Pratley

unread,
Nov 5, 2009, 9:44:22 PM11/5/09
to Clojure

On Nov 6, 12:51 pm, Daniel Janus <nath...@gmail.com> wrote:
> As another example, consider multiplying the first 42 elements of a
> list of numbers by 42, and leaving the rest unchanged. It's much more
> straightforward for me to write (and then read)
>
> (iter (for x in lst)
>      (for i from 0)
>      (collect (if (< i 42) (* x 42) x)))

How about this version?
user=> (def lst [1 2 3 4 5 6 7 8])
user=> (concat (map #(* % 42) (take 4 lst)) (drop 4 lst))
(42 84 126 168 5 6 7 8)

Don't get me wrong - I like the macro - just pointing out how I'd
write this particular use case.

hoeck

unread,
Nov 6, 2009, 3:10:00 AM11/6/09
to Clojure

Hi Daniel,

I too like the iter macro so much that I've written my own version for
clojure (http://github.com/hoeck/clojurebox2d/blob/master/hoeck/
iterate.clj :).
I use it to make it easier to work with the JBox2D physics engine,
which is a a Java port from Box2D (written in C++) and thus often uses
C++ Idioms like plain Objects with .getNext to traverse a list and
more arrays than iterators.
Also, traversing an array and picking/collecting/modifying some of its
contents is way simpler in an iter clause than writing a clojure loop.
So, I'm not really using it as a replacement for map, filter, reduce
and friends, but as a more convenient replacement to loop/recur and
areduce. Maybe, if there is some interest in it, we could make a
unified iterate implementation?

Erik

ataggart

unread,
Nov 6, 2009, 5:44:36 AM11/6/09
to Clojure


On Nov 5, 5:51 pm, Daniel Janus <nath...@gmail.com> wrote:
>
> (iter (for x in lst)
>      (for i from 0)
>      (collect (if (< i 42) (* x 42) x)))
>
> than something along the lines of
>
> (map (fn [[i x]] (if (< i 42) (* x 42) x))
>    (map vector (iterate inc 0) lst))

(concat (take 42 (map #(* 42 %) lst))
(drop 42 lst))

John Harrop

unread,
Nov 6, 2009, 9:44:56 AM11/6/09
to clo...@googlegroups.com
And that's circumventing that (map vector (iterate inc 0) lst) completely, rather than just replacing it with (indexed lst) (the indexed function is in clojure.contrib.seq-utils, as is the reductions function I used earlier in this thread).
Reply all
Reply to author
Forward
0 new messages