5

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;
}
4

3 回答 3

8

该函数作为第一个代码块中调用时counter的第一个参数传递。reducereduce函数中,第一个参数称为combine。然后使用参数调用它baseelement这是您正在寻找的神秘参数!

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

编辑:阐述

逻辑流

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

编辑:进一步阐述

我更新了图像,以更好地解释来自数组的元素。

所以i更容易理解,在调用函数0时创建,并将名称作为参数分配,然后作为/函数的结果重新分配,该函数返回可能的增量。reducebasecountercombinebase

ii以传递给 的数组的形式开始生命countZeroes,然后沿着链传递,直到它被循环迭代,forEach循环提取单个element并在其上操作combine函数(与 一起base)。

于 2012-11-02T21:54:58.270 回答
7

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

减少的工作原理

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

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

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;
}

从图中可以看出,最初与 一起0传递给函数,这表示构造内的第一次“迭代” 。然后将结果值写回.baseelementxs[0]forEachbase

正如您在可视化中看到的,0是函数的左参数counter(),其结果随后作为左参数传递给counter()。由于base/total充当forEach构造中的左参数,因此将该值写回 是有意义的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]);
}

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

在您的情况下,这是通过以下方式传递actionforEach()

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

它是每次调用时定义的匿名函数reduce()。每次迭代element“真的”一次也是如此。array[i]for

于 2012-11-02T22:03:26.363 回答
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));
于 2012-11-02T22:02:43.227 回答