了解Eloquent Javascript的Reduce功能

KMc*_*McA 5 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);
}
Run Code Online (Sandbox Code Playgroud)

早期的例子来自文本:

function reduce(combine, base, array) {
 forEach(array, function (element) {
    base = combine(base, element);
  });
  return base;
}
Run Code Online (Sandbox Code Playgroud)

Bil*_*oon 8

该函数counter作为第一个reduce代码块中调用的第一个参数传递.在reduce函数内,第一个参数称为combine.然后用参数调用它base,element这是你正在寻找的神秘论据!

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

编辑:详细说明

逻辑流程

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

编辑:进一步阐述

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

所以i更容易遵循,在调用函数0时创建reduce,并base在重新分配作为counter/ combinefunction 的结果之前将名称作为参数分配,该函数返回base可能的增量.

ii在传递给数组时开始生命countZeroes,然后沿链传递,直到它被forEach循环迭代,循环提取单个element并在其combine上运行函数(连同base).


pha*_*t0m 7

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

如何减少工作

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

                   return value of reduce()
                   /
                 etc ...
                /
            counter
           /       \
       counter      xs[2]
      /       \
  counter      xs[1]
 /       \
0         xs[0]
Run Code Online (Sandbox Code Playgroud)

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

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

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

将此概念映射到代码

function reduce(combine, base, array) {
 forEach(array, function (element) {
    base = combine(base, element);
  });
  return base;
}
Run Code Online (Sandbox Code Playgroud)

从图中可以看出,最初与函数一起0传递base给函数element,其表示构造xs[0]内的第一个"迭代" forEach.然后将结果值写回base.

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

element来自哪里

Eloquent JavaScript的第6章开始,这里是forEach()(在注释中,我action用最终值替换了调用).

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

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

在您的情况下,这传递actionforEach():

function (element) {
    base = combine(base, element);
}
Run Code Online (Sandbox Code Playgroud)

它是每次调用时定义的匿名函数reduce().所以每次迭代element都是"真的" array[i]一次for.