了解Eloquent Javascript Reduce功能

时间:2012-11-02 21:49:26

标签: javascript reduce

Eloquent Javascript 中,作者要求读者编写一个函数countZeroes,它以数组数作为参数,并返回其中出现的零的数量。使用 reduce 函数的示例。

我知道

  • reduce函数的概念是采用数组并将其转换为单个值。
  • 三元运算符正在做什么,这是函数的基本部分。

我不知道

  • 其中计数器函数的参数来自。

从书中可以看出:

function countZeroes(array) {
  function counter(total, element) { // Where are the parameter values coming from?
    return total + (element === 0 ? 1 : 0);
  }
  return reduce(counter, 0, array);
}

文本中的早期示例:

function reduce(combine, base, array) {
 forEach(array, function (element) {
    base = combine(base, element);
  });
  return base;
}

3 个答案:

答案 0 :(得分:8)

在第一个代码块中调用时,函数counter作为reduce的第一个参数传递。在reduce函数中,第一个参数称为combine。然后使用参数baseelement来调用它们,这些是你正在寻找的神秘论据!

所以棘手的一点是,函数不会在定义和命名的地方执行,而是作为参数传递给reduce函数,然后执行它。

编辑:阐述

logic flow

所以......函数被定义并命名(在第1点),然后定义被传递,而没有名称到另一个函数(在第2点)以及变量(我称为i和ii),它被拾取被调用之前的第一个参数(在第3点)的名称(在第4点)以及其他参数

编辑:进一步阐述

我更新了图像,以便更好地解释来自阵列的元素。

因此i更容易理解,在调用0函数时创建为reduce,并在重新分配之前将名称base指定为参数counter / combine函数的结果,它以可能的增量返回base

ii以传递给countZeroes的数组开始生活,然后沿着链传递,直到被forEach循环迭代,从而提取单个element并在其上运行combine函数(以及base)。

答案 1 :(得分:7)

查看代码,只有一个可能的答案:由于函数counter仅在传递给reduce()时被引用一次,因此reduce必须为函数提供参数。

如何减少

以下是reduce如何工作的可视化。您需要从上到下阅读diagrom,/\表示哪些参数(如下)传递给上面的counter()函数。

                   return value of reduce()
                   /
                 etc ...
                /
            counter
           /       \
       counter      xs[2]
      /       \
  counter      xs[1]
 /       \
0         xs[0]

counter()最初提供0(毕竟,当你没有处理任何元素时,看到的零的初始总量只有零)和第一个元素。

如果数组的第一个元素为零,则0增加1。看到第一个元素后的新总数然后由counter(0, xs[0])返回到reduce()函数。只要剩下元素,它就会将此值作为新的待处理总计传递给counter()函数,以及数组中的下一个元素:xs[1]

只要数组中有元素,就会重复此过程。

将此概念映射到代码

function reduce(combine, base, array) {
 forEach(array, function (element) {
    base = combine(base, element);
  });
  return base;
}

如图所示,0最初通过base传递给函数,同时element表示xs[0]第一次“迭代”在forEach构造内。然后将结果值写回base

正如您在可视化中所看到的,0是函数counter()的左参数,其结果随后作为左参数传递给counter()。由于base/total充当forEach构造中的左参数,因此将该值写回base/total是有意义的,因此之前的结果将再次传递给counter()/combine()下一次迭代。 / s的路径是base/total的“流程”。

element来自

chapter 6 of Eloquent JavaScript开始,以下是forEach()的实现(在评论中,我已将action的调用替换为最终值。)

function forEach(array, action) {
  for (var i = 0; i < array.length; i++)
    action(array[i]); // <-- READ AS: base = counter(base, array[i]);
}

action表示一个函数,在每个元素的简单for循环内调用,同时迭代array

在您的情况下,这会以action传递给forEach()

function (element) {
    base = combine(base, element);
}

这是每次调用reduce()时定义的匿名函数。因此,对于element的每次迭代,array[i]都是“真的”for

答案 2 :(得分:1)

reduce方法传递参数。reduce函数源代码中的某处可能是这样的:

function reduce(iterator, memo, collection) { // in your example, iterator here is your counter function
    …
    // while looping through the collection you passed in
    memo = iterator(memo, collection[i], i); // it calls your counter function and
                                             // passes the total and the current array
                                             // element as well as the current index,
                                             // then stores the result to be passed in
                                             // with the next array element
    …
    return memo;
}

附录

也许这有助于更好地说明(jsfiddle):

// very simple reduce implementation
function reduce(iterator, memo, collection) {
    for (var i = 0; i < collection.length; i++) {
        memo = iterator(memo, collection[i], i);
    }
    return memo;
}

zeroes = [1,0,0,1,1,0,1];
function counter(total, element) { return total + (element === 0 ? 1 : 0); }

alert(reduce(counter, 0, zeroes));