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

时间:2012-07-05 16:11:44

标签: clojure reduce accumulator

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

总是是累加器吗?

有时是累加器吗?

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

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

链接到博客条目:

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

如何简单:(减少+ [1 2 3])?是否涉及累加器?

我认为我的问题是:“什么是累加器?”

但我仍然想了解累加器和 reduce 函数之间的关系。所以对这些特定(相关)问题的任何答案都是最受欢迎的!

6 个答案:

答案 0 :(得分:5)

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

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

例如:

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

如何简单:(减少+ [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]

答案 1 :(得分: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))

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

答案 2 :(得分:3)

它可以是累加器。

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

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

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

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

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

这是一个减少,你不积累任何东西,所以很难说第二个参数是累加器:

(reduce 
  (fn [_ x] (println x))
  nil 
  [1 2 3])

答案 3 :(得分:2)

它总是累积器吗?

是的,它总是一个累加器。累加器在其进行时保持计算的中间值,并且当计算结束时累加器具有计算的最终结果。累加器是可变的还是不可变的,它是累加器的不同方面,但这就是累加器在概念上的含义。

它有时候是累加器吗?

不,它始终是reduce中的累加器,因为reduce AKA fold的整个概念是将值列表转换为单值,并且您需要一个累加器如果列表中下一个元素的处理需要处理列表的前一个元素的结果,那么进行这样的计算等等。

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

val视为f的第一个参数在概念上是不正确的,因为如果是这种情况,那么f应始终获得最初指定的val,这就像创建f的部分函数,​​第一个参数为val。每次拨打f都会获得val作为之前调用f的返回值。因此val是累加器。

答案 4 :(得分:0)

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

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

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


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


是的,它总是 累加器。 reduce整点是它允许你以功能和不可变的方式进行累积。

我会注意到,有可能以积累无关紧要的方式使用reduce,就像迈克拉的回答一样。在这种情况下,reduce 进行累积(内部),但它反复使用相同的值,因此没有明显的效果。


仅使用两个参数调用reducerules that Clojure uses有点复杂,但归结为是......

(reduce + [1 2 3])

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

(reduce + 1 [2 3])

你问过累加器是什么。累加器是循环数据时累积数据的概念

在命令式语言中,累加器通常是在循环时变异的变量。让我们看看莱昂内尔的例子,稍作修改:

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. 序列
  4. 它使用两个参数调用转换函数:

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

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

      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函数看起来非常类似于之前的命令式代码。

      现在让我们在Clojure中以功能的方式实现reduce,没有变异:

      (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是否以可变或不可变的方式进行积累, 进行积累。


      对于funsies,有趣的是Clojure使用reverse实现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和其他内容。但我认为这有点超出了你的问题。

答案 5 :(得分: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为空时,它将作为函数的值返回。

某些东西是否是累加器取决于函数的 body

例如,如果我们以这种方式实施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最初绑定到()。但是说()是一个累加器是没有意义的。