首页 > 解决方案 > 将这个 compose 函数传递给 Javascripts reduce 的执行流程是什么?

问题描述

我只想知道在以下代码的情况下reduce是如何工作的(这是由stackoverflow用户在我之前的问题中提供的,我问这个问题是因为他的代码片段导致我有更多的问题没有被清除并且太长,不适合评论部分)。一个函数数组被传递到一个reducer。有一个 compose 函数在函数数组上运行。根据我的理解,这个 f 是累加器,而 g 是数组中的下一项。reduce 的每个周期返回的内容成为下一个周期的累加器。如果没有传入 reduce 的 initalValue 参数,则数组中的第一项将用作初始累加器值。

const compose = (f, g, i) => (...args) =>  {
  console.log(i, g);
  console.log(i, f);
  return f(g(...args));
}

const f_xe = (x) => x + 'e', 
      f_xd = (x) => x + 'd', 
      f_xc = (x) => x + 'c',
      f_xy = (x, y) => x + y;

console.log([f_xe, f_xd, f_xc, f_xy].reduce(compose)('a','b'));

// 3 [Function: f_xy]
// 3 [Function]
// 2 [Function: f_xc]
// 2 [Function]
// 1 [Function: f_xd]
// 1 [Function: f_xe]
// abcde

我把它想象成这样:

cycle #1:
f = f_xe
g = f_xd
return f(g(...args))
^ which is f_xe(f_xd('a', 'b'))

cycle #2:
f = what was returned previously
^^ which will be f_xe(f_xd('a', 'b'))

g = f_xc

return f(g(...args))
^^ which is f_xe(f_xd('a', 'b'))(f_xc('a', 'b'))

我已经知道这种思路是错误的,因为流程以封装的方式工作,就像这样:f_xe(f_xd((f_xc(f_xy('a', 'b'))))) 但为什么会这样。如果有人可以错综复杂地解释为什么它以这种方式包装并逐步分解reduce的每个循环,那将不胜感激。我想知道的另一件事是,为什么不尝试在第一个周期立即评估?f_xe(f_xd('a', 'b'))当返回这段代码时,它不会尝试评估它并产生错误,而不是继续处理数组中的下一项吗?相反,代码从数组中的最后一项开始计算,即使 compose 函数被指示从头开始应用。我确实理解,对于组合函数,最后一项将首先运行,然后以此类推,但是控制台日志语句不应该按从前到后的顺序运行吗?

再一次,我知道我的思路与这个完全不同,但我希望如果我分享我的思路,有人可以把它推向正确的方向。感谢任何可以对此有所了解的人。

标签: javascriptfunctionfunctional-programmingclosuresreduce

解决方案


首先忘记'a'and'b'参数。重要的部分是

const f = [f_xe, f_xd, f_xc, f_xy].reduce(compose);

这是我们需要关注的,也是我们可以应用 for 定义的地方reduce。的呼唤f('a','b')稍后来。

扩展reduce调用时,我们发现

const f = compose(compose(compose(f_xe, f_xd, 1), f_xc, 2), f_xy, 3);

(这实际上有点奇怪。我建议使用reduceRight组合函数。还将识别函数作为累加器的初始值传递。)

现在我们可以扩展compose调用:

const f1 = (...args) => {
  console.log(1, f_xe);
  console.log(1, f_xd);
  return f_xe(f_xd(...args));
}
const f2 = (...args) => {
  console.log(2, f1);
  console.log(2, f_xc);
  return f1(f_xc(...args));
}
const f3 = (...args) => {
  console.log(3, f2);
  console.log(3, f_xy);
  return f2(f_xy(...args));
}
const f = f3;

现在,当您调用 时f3('a', 'b'),您可以看到日志为什么会“向后”发生。

控制台日志语句不应该按从前到后的顺序运行吗?

如果你想要这样,你最好把它们放在compose函数中,而不是放在它返回的闭包中。尝试

const compose = (f, g, i) =>  {
  console.log(i, g);
  console.log(i, f);
  return  (...args) => f(g(...args));
}

推荐阅读