Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Map with an accumulator in Clojure?

Tags:

clojure

I want to map over a sequence in order but want to carry an accumulator value forward, like in a reduce.

Example use case: Take a vector and return a running total, each value multiplied by two.

(defn map-with-accumulator
  "Map over input but with an accumulator. func accepts [value accumulator] and returns [new-value new-accumulator]."
  [func accumulator collection]
  (if (empty? collection)
    nil
    (let [[this-value new-accumulator] (func (first collection) accumulator)]
      (cons this-value (map-with-accumulator func new-accumulator (rest collection))))))

(defn double-running-sum
  [value accumulator]
  [(* 2 (+ value accumulator)) (+ value accumulator)])

Which gives

(prn (pr-str (map-with-accumulator double-running-sum 0 [1 2 3 4 5])))

>>> (2 6 12 20 30)

Another example to illustrate the generality, print running sum as stars and the original number. A slightly convoluted example, but demonstrates that I need to keep the running accumulator in the map function:

(defn stars [n] (apply str (take n (repeat \*))))

(defn stars-sum [value accumulator]
  [[(stars (+ value accumulator)) value] (+ value accumulator)])

(prn (pr-str (map-with-accumulator stars-sum 0 [1 2 3 4 5])))
>>> (["*" 1] ["***" 2] ["******" 3] ["**********" 4] ["***************" 5])

This works fine, but I would expect this to be a common pattern, and for some kind of map-with-accumulator to exist in core. Does it?

like image 669
Joe Avatar asked Dec 16 '25 11:12

Joe


1 Answers

You should look into reductions. For this specific case:

(reductions #(+ % (* 2 %2)) 2 (range 2 6))

produces

(2 6 12 20 30)

like image 125
Diego Basch Avatar answered Dec 19 '25 07:12

Diego Basch



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!