在Clojure中使用累加器映射?

时间:2014-05-24 20:15:11

标签: clojure

我想按顺序映射序列,但是想要向前传递累加器值,就像在reduce中一样。

示例用例:获取一个向量并返回一个运行总计,每个值乘以2。

(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)])

哪个给出了

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

>>> (2 6 12 20 30)

另一个例子来说明一般性,打印运行总和为星和原始数。一个稍微复杂的例子,但证明我需要在map函数中保持正在运行的累加器:

(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])

这种方法很好,但我希望这是一种常见的模式,并且map-with-accumulator中存在某种core。是吗?

4 个答案:

答案 0 :(得分:4)

您应该查看reductions。对于这个具体案例:

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

产生

(2 6 12 20 30)

答案 1 :(得分:3)

一般解决方案

函数捕获可依赖于项目和序列累积和的映射的通用模式

(defn map-sigma [f s] (map f s (sigma s)))

其中

(def sigma (partial reductions +))

...返回序列累加和的序列:

(sigma (repeat 12 1))
; (1 2 3 4 5 6 7 8 9 10 11 12)

(sigma [1 2 3 4 5])
; (1 3 6 10 15)

map-sigma的定义中,f是两个参数的函数,该项后跟累加器。

示例

在这些术语中,第一个例子可以表达

(map-sigma (fn [_ x] (* 2 x)) [1 2 3 4 5])
; (2 6 12 20 30)

在这种情况下,映射函数忽略该项并仅依赖于累加器。

第二个可以表达

(map-sigma #(vector (stars %2) %1) [1 2 3 4 5])
; (["*" 1] ["***" 2] ["******" 3] ["**********" 4] ["***************" 5])

...映射函数依赖于项目和累加器。

没有像map-sigma这样的标准功能。

一般性结论

  • 仅仅因为计算模式很常见并不意味着这一点 它值得或需要自己的标准功能。
  • 懒惰的序列和序列库足够强大,可以挑逗 将许多问题分成清晰的功能组合。

重写以特定于提出的问题。


编辑以适应更改的第二个示例。

答案 2 :(得分:0)

减少是一种方式,因为迭戈提到你的具体问题,以下工作

(map #(* % (inc %)) [1 2 3 4 5])

答案 3 :(得分:0)

如上所述,您可以使用reductions

(defn map-with-accumulator [f init-value collection]
  (map first (reductions (fn [[_ accumulator] next-elem]
                           (f next-elem accumulator))
                         (f (first collection) init-value)
                         (rest collection))))

=> (map-with-accumulator double-running-sum 0 [1 2 3 4 5])
(2 6 12 20 30)

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

仅在您想要保留原始要求的情况下。否则,我更倾向于将f分解为两个单独的函数并使用缩略图的方法。