Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clojure: reduce with three parameters

Tags:

clojure

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

like image 247
pistacchio Avatar asked Jul 20 '12 12:07

pistacchio


2 Answers

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))
like image 126
kotarak Avatar answered Nov 20 '22 20:11

kotarak


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))))
like image 33
amalloy Avatar answered Nov 20 '22 20:11

amalloy