Mapping every second item

utilities — cgrand, 17 April 2009 @ 10 h 58 min

I wanted to apply a function to every second item in a coll. I was considering writing something using interleave, take-nth and map or a combination of mapcat and partition when I thought of this:

(map #(%1 %2) (cycle [f identity]) coll)

I really love clojure’s map parallel processing. (I should ask if every? and some could be allowed to take several colls.)



    I really enjoy reading the examples you post.

    Question: Suppose ‘coll’ is a collection of 10,000 items, this line of code will create another collection using ‘cycle’ of 10,000 items. Isn’t it a bit expensive from performance point of view?

    Comment by michaelr — 18 April 2009 @ 2 h 29 min
  2. Hi Michael,

    cycle would not create a 10,000-items collection but would lazily realize the first 10,000 conses of an infinite seq (and would not keep a reference to them, making them GCable as soon as they are processed so they have a very short life-span).

    I didn’t benchmark them but one can count how many transient objects are created (since it’s what worries you) by each other approach:

    (interleave (map f (take-nth 2 coll)) (take-nth 2 (rest coll))) would create 3*5,000 transient conses (each take-nth + map) — and I’m not counting seq being called twice on coll.

    (mapcat (fn [[a b]] [(f a) b]) (partition 2 coll)) would create 3*5,000 conses (partition yields a seq of 5000 seqs of 2 conses), the map part of mapcat would create 5000 conses and 5000 vectors. The concat part would create 10000 transient conses (seq on each vector)

    NB: I used “cons” rather liberally to mean “objects which implement clojure.lang.ISeq”.

    NB2: A quick pointless benchmark on (range 100000) seems to show that (map #(%1 %2) (cycle [...]) coll) is indeed faster but what I was after when I wrote this post is conciseness.

    Comment by Christophe Grand — 18 April 2009 @ 9 h 08 min
  3. Michael,

    You can redefine cycle using seq-utils/rec-seq:
    (defn my-cycle [coll] (rec-seq c (concat coll c))) which create a cyclic seq of only two conses.

    (map #(%1 %2) (my-cycle [...]) coll) is obviously faster: no garbage trumps easily collectable garbage.

    Comment by Christophe Grand — 18 April 2009 @ 9 h 16 min
  4. Christophe – as always, wonderfully elegant example. Just what I needed, thank you.

    Comment by Daniel — 5 April 2011 @ 19 h 25 min
  5. You said every second item .. so the cycle should be (cycle [identity f]) :)

    Comment by Ankur Dhama — 20 April 2012 @ 18 h 23 min

RSS feed for comments on this post. TrackBack URI

Leave a comment

(c) 2024 Clojure and me | powered by WordPress with Barecity