11

当你调用reduce并传递一个函数和两个参数时,第一个参数可以被认为是一个累加器吗?

总是一个累加器吗?

有时是累加器吗?

我正在阅读一篇关于使用 Clojure 解析大文件的博客文章,发现这一行:

(reduce line-func line-acc (line-seq rdr))

链接到博客条目:

http://lethain.com/reading-file-in-clojure/

一个简单的:(reduce + [1 2 3])怎么样?有没有累加器?

我认为我的问题是:“究竟什么是蓄能器?”

但我仍然想了解累加器和reduce函数之间的关系。因此,欢迎对这些特定(相关)问题的任何回答!

4

6 回答 6

5

在 (reduce f val coll) 中,val 是累加器吗?

,这是函数 f 的参数。这意味着f应用于 val 和 coll 中的第一个元素。

例如:

(reduce + 1 [2 3 4])        ;; 10
(reduce + (cons 1 [2 3 4])) ;; 10

一个简单的:(reduce + [1 2 3]) 怎么样?有没有累加器?

,它是一系列功能的应用f;像这样:

(reduce f [1 2 3 4]) ; ==> (f (f (f 1 2) 3) 4)
(reduce f 1 [2 3 4]) ; ==> (f (f (f 1 2) 3) 4)

请注意,在这两种情况下,最里面的调用都f需要参数 1 和 2 ?在第一种情况下,1 和 2 是 coll 的第一个和第二个元素;在第二种情况下,1 是唯一值,2 是 coll 的第一个元素。

究竟什么是蓄能器?

累加器是保存计算中间结果的变量。就像在这个 Java 片段中一样:

int sum = 0;
for (int i = 0; i < 10; i++) {
    sum += i;
}
return sum;

这里,变量 sum 的值随着循环的进行而改变。在 Clojure 中,变量是不可变的,所以你看不到这个习语。相反,累加器更经常(但不总是)是递归函数的参数。

例如,这里有一个函数,它通过将列表中的第一个条目“累积”到累加器的前面来反转列表。在这种情况下,变量没有改变,而是传递给函数的另一个调用。

(defn reverse [[f & r] acc]
  (if (nil? f)
    acc
    (recur r (conj acc f))))

(reverse [1 2 3] ()) ;; [3 2 1]
于 2012-07-05T16:31:51.060 回答
5

它可以是一个累加器。

这取决于你如何使用它,也取决于你对“累加器”的定义。

这是一个传统的可变累加器,请注意需要在每一步继续传递相同的累加器:

(reduce 
  (fn [atom val] (do (swap! atom + val) atom))
  (atom 10)
  [1 2 3 4 5])
=> #<Atom@115872f5: 25>

这是与不可变的“累加器”一起使用的 reduce。尽管累加器传统上是可变的,但我认为大多数函数式程序员会将其定义为累加器:

(reduce + 10 [1 2 3 4 5])
=> 25

这是一个不累积任何东西的 reduce,所以很难证明第二个参数是一个累加器:

(reduce 
  (fn [_ x] (println x))
  nil 
  [1 2 3])
于 2012-07-06T10:59:43.303 回答
4

我假设最初的问题是使用累加器作为通用术语,而不是语言中使用的官方术语。

我不知道函数之后的第一个参数(第二个参数)是否会被称为 Clojure 术语中的累加器。但它确实似乎是这样行事的。

在下面的:

(defn reduce-csv-row
     "Accepts a csv-row (a vector) a list of columns to extract, 
     and reduces the csv-row to a smaller list based on selection
     using the values in col-nums (a vector of integer vector 
     positions.)"

    [csv-row col-nums]

    (reduce
        (fn [filter-csv-row col-num]

            ;Don't use vectors without the proper information in them.

            (if-not (<= (count csv-row) 1)
                (conj filter-csv-row (nth csv-row col-num nil))))
        []
        col-nums))

我当然希望调用这个函数后会返回一个序列,所以累加器可能不是一个坏词,但作为一个官方术语,我不能说。

于 2012-07-05T16:33:07.313 回答
2

它总是一个累加器吗?

是的,它始终是一个累加器。累加器是在计算过程中保存计算的中间值的东西,当计算结束时,累加器具有计算的最终结果。累加器是可变的还是不可变的,这是累加器的不同方面,但这就是累加器在概念上的含义。

有时是累加器吗?

不,它始终是 a 中的累加器,因为AKAreduce的整个概念是将值列表转换为单个值,如果处理列表中的下一个元素需要处理结果,则确实需要一个累加器来进行此类计算列表的前一个元素等等。reducefold

当您不传递累加器初始值(即函数签名val中的部分reduce)时,累加器的初始值设置为列表的第一个元素,处理将从列表的第二个元素开始。

val视为 的第一个参数在f概念上是不正确的,因为如果是这种情况,那么f应该总是得到val最初指定的相同,这就像创建fwith first param as的偏函数val。每次调用都fval作为调用的前一个返回值f。因此val是一个累加器。

于 2012-07-06T04:15:32.163 回答
0

Ankur 的回答是正确的。另外,我认为这个链接很好地解释了事情:

http://www.learningclojure.com/2010/08/reduce-not-scary.html

现在,回答你的问题...


是的。第二个参数reduce是累加器的初始值。


是的,它始终是一个累加器。的全部意义在于reduce它可以让你以一种功能性和不可变的方式进行积累。

我会注意到,可以以reduce累积无关紧要的方式使用,就像在 mikera 的回答中一样。在那种情况下,reduce仍然做累加(内部),但它一遍又一遍地使用相同的值,所以它没有明显的效果。


reduce只使用两个参数调用时...... Clojure 使用的规则有点复杂,但归结为......

(reduce + [1 2 3])

...将使用序列的第一个元素作为初始值,这意味着它与此相同:

(reduce + 1 [2 3])

你问什么是蓄能器。累加器是在循环某些东西时累积数据的概念

在命令式语言中,累加器通常是一个在循环时发生变异的变量。让我们看一下 Leonel 的示例,稍作修改:

var sum = 0;
for (var i = 0; i < 10; ++i) {
    sum = sum + i;
}
return sum;

起初,这似乎不可能以功能性的方式完成。但是reduce,你可以!

(reduce (fn [sum i] (+ sum i)) 0 (range 0 10))

这是如何工作的,reduce需要三个参数:

  1. 转换函数
  2. 累加器的初始值
  3. 一个序列

它使用两个参数调用转换函数:

  1. sum是累加器的当前值
  2. i是序列的当前元素

现在,无论转换函数返回什么,都将用作累加器的当前值。换句话说......第一次迭代sum将是初始值。在第一次迭代之后,是sum一次迭代中返回的转换函数。

reduce如果我在 JavaScript 中编写一个使用突变的实现,也许会有所帮助:

function reduce(f, init, coll) {
    for (var i = 0; i < coll.length; ++i) {
        init = f(init, coll[i]);
    }
    return init;
}

reduce(function (sum, i) { return sum + i }, 0, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);

如您所见,该reduce函数看起来与之前的命令式代码非常相似。

现在让我们reduce在 Clojure 中以一种没有变异的函数方式实现:

(defn reduce [f init coll]
  (if-let [s (seq coll)]
    (reduce f (f init (first s)) (rest s))
    init))

(reduce (fn [sum i] (+ sum i)) 0 (range 0 10))

但是不管是reduce可变的还是不可变的积累,都是在做积累。


有趣的是,值得注意的是 Clojurereverse使用以下方法实现reduce

(defn reverse
  "Returns a seq of the items in coll in reverse order. Not lazy."
  {:added "1.0"
   :static true}
  [coll]
    (reduce1 conj () coll))

弄清楚为什么会这样是一个有趣的心理练习。

你也可以做一些简洁的事情,比如使用 reduce 实现 map、filter 和其他东西。但我认为这超出了你的问题。

于 2014-04-18T07:11:41.483 回答
0

(reduce f x y)
  • 总是x一个累加器?不。
  • x有时是蓄能器吗?

  • x是提供给 的初始值reduce

究竟什么是蓄能器?

累加器是作为递归函数的值返回的本地绑定。

例如,如果我们要实现reduce自己,我们可能会这样做:

(defn reduce [f acc coll]
  (if (empty? coll)
    acc
    (recur f (f acc (first coll)) (rest coll))))
  • acc是一个累加器:作为一个参数,它是一个本地的。
  • coll为空时作为函数的值返回。

某物是否是累加器取决于函数的主体

例如,如果我们这样实现reverse

(defn reverse [coll]
  (loop [in coll, out ()]
    (if (seq in)
      (recur (rest in) (cons (first in) out))
      out)))

... 然后out是一个累加器。

但是如果我们这样实现:

(defn reverse [coll]
  (reduce conj () coll))

...没有累加器。

reduce调用内部,acc最初绑定到(). 但说这()是一个累加器是没有意义的。

于 2014-09-05T07:48:52.550 回答