I often find myself in need of a kind of expanded reduce
whenever I must process an item at time (like reduce), accumulate some kind of result (like reduce) but do the process based on the previous item of the sequence (unlike reduce).
For example (a silly one), add 1 to the accumulator if both the current item and the previous one are even and subtract one of they're odd. This is just a stupid case, but I've run into this kind of problems often. I generally make a vector as accumulator, so that the first item is the real aggregation and the second one is the previous item. This is not very elegant and certainly verbose.
Is there a core function to help in those cases? What is the most idiomatic way to deal with such problem? Thanks
partition
to the rescue.
(reduce (fn [i [a b]]
(cond
(and (even? a) (even? b)) (inc i)
(and (odd? a) (odd? b)) (dec i)
:else i))
0 (partition 2 1 input))
Or a little more concise:
(reduce (fn [i pair]
(condp every? pair
even? (inc i)
odd? (dec i)
i))
0 (partition 2 1 input))
For this particular problem, I recommend kotarak's solution, using partition to track previous elements. But in the general case, where you need to manage some state in addition to your reduce's eventual "answer", you can simply reduce over a pair, or a map or whatever, and at the end get out the accumulator value. For example:
(defn parity [coll]
(first (reduce (fn [[acc prev] x]
[(cond (and (even? prev) (even? x)) (inc acc)
(and (odd? prev) (odd? x)) (dec acc)
:else acc)
x])
[0 (first coll)], (rest coll))))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With