在Clojure中使用累加器的映射?

在Clojure中使用累加器的映射?,clojure,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 (

我想按顺序映射一个序列,但想向前携带一个累加器值,就像在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])

这很好,但我希望这是一种常见的模式,并且在
core
中存在某种带有累加器的
映射。是吗?

你应该研究一下减少量。对于这一具体情况:

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


(2 6 12 20 30)

正如迭戈所提到的那样,减排是一条路,但是对于您的具体问题,以下方法可行

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

一般解决方案

映射的公共模式可以同时依赖于项和序列的累积和,由函数捕获

(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 (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
这样的标准函数

一般结论

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

重写为特定于提出的问题



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

如前所述,您可以使用
缩减


这只是在你想保持原有要求的情况下。否则,我宁愿将
f
分解为两个独立的函数,并使用缩略图的方法。

谢谢。不过,我不确定这是否抓住了我问题的一般性(我必须对其进行说明,但说明可能没有充分展示一般性)。我添加了另一个例子。很抱歉延迟接受,我想确保这涵盖了我的用例。是的,谢谢,谢谢。这解决了“我如何计算这个数字序列”而不是“这个高阶函数存在吗?”。我想这是我举个例子的错。无论如何,我已经添加了另一个来说明。谢谢你的回答,但我认为这回答了示例,而不是实际问题(运行累加器可用的地图)。我已经编辑了星星来演示这一点(注意,我没有更改我要问的问题或函数!)。@Joe-改编为使运行的累加器可用于任何映射。非常感谢您的回答和努力。我接受了另一个,因为他是第一个,对不起。如果可以,我会加倍投票@乔:没问题。我必须自己把它做好。洞察是参数化:一旦看到就显而易见,但更难捕捉。关于函数式编程,问题的完美表达就是答案。