I ran into a problem that seem 4Clojure-ish the other day, and failed to figure it out. Suppose you have a sequence something like this:
[A D B E C F]
and you want to end up with something like
[[A B C] [D E F]]
Here’s the catch: each item is actually a rich hiccup vector, meaning you can’t do a plain sort. I managed to get them in the right order with partition and interleave, but then I had too many layers of [ and ( on them. So, with only their ORDER to go on, how can they be re-ordered so all the odd are in one container, all the even in the other?
EDIT: I marked the code as solution which gave one of the several good answers below, but readers who don’t care for all the “entertainment” of reading the whole thread should also see this great answer about the principle of transducers: Sorting a rich sequence without (sort) - #43 by outrovurt
It can be very useful…basically creates and abstract “reducible” thing that implements the recipe as if by comp (the comp is implicit). So you can define an object that represents a potential reduction, which in turn can be transduced/reduced/whatever. It won’t make intermediate collections, etc. Very useful for composing transducing stuff; blurs the line between seq composition and transducers.
(defn order-odd-even
[coll]
(loop [[a b & more] coll
left []
right []]
(let [left (conj left a)
right (conj right b)]
(if more
(recur more left right)
[left right]))))
I had absolutely no idea you could pass eduction a lazy-sequence like you have …
… wait a minute: @joinr I think you may have made a small code-typo: shouldn’t that (range n) be outside of the (map ,,,) form in intervec? When I copied and pasted into my editor it appears as if you are just passing a lazy-sequence returned by map to the eduction.
let out = [[],[]];
let input = ["A","D","B","E","C","F"];
for(let i = 0; i < input.length; ++i){
let e = input[i];
out[i % 2].push(e);
};
console.log(out)
or in pseudo lisp:
(var out [[] []])
(var input ["A" "D" "B" "E" "C" "F"])
(for:array [[i e] input]
(arr-push (. out [(mod i 2)]) e)))
out
Yeah, typo (now fixed). eduction still works with lazy seqs, it will just fall back to a reduce over the seq (and in the case of the original code I posted, the lazy map will generate an intermediate chunked seq). The intended way is to shift one paren and keep the map isolated, so the eduction is applied to a range, which is both seqable and reducible, where the reducible form uses no intermediate collections.
(->> '[A D B E C F]
(transduce (map-indexed vector)
(completing
(fn [[l r] [idx item]]
(if (even? idx) [(conj l item) r] [l (conj r item)])))
[[] []]))
Something I should have mentioned is that the actual number of items in the coll is unknown; only that it is even is known. It looks like this solution is hard-coded expecting 6 items, right?
Pro Tip - You can also do mutation by using a vector in an atom in order to be fully clojure compliant.
Regarding complecting, eduction, etc… It’s much easier to read the source rather than try to intuit what the words mean for most. of the transducer stuff.
You’ll also find that iterators do about 150% of what transducers want to do.