我想按顺序映射序列,但是想要向前传递累加器值,就像在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
。是吗?
答案 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
分解为两个单独的函数并使用缩略图的方法。