【问题标题】:How Javascript `reduce` performed on function array achieves function composition?对函数数组执行的Javascript`reduce`如何实现函数组合?
【发布时间】:2019-03-24 10:38:18
【问题描述】:

我在 redux compose 函数中遇到了这种模式。我仍然不明白在下面的示例中,函数是如何从最后一个而不是从第一个开始评估的:

function f2(a) {
  return a + a;
}
function f3(a) {
  return a + a + a;
}
function f4(a) {
  return a + a + a + a;
}
function f5(a) {
  return a + a + a + a + a;
}

function compose(...funcs) {
  return funcs.reduce(function x(a, b) {
    return function y(...args) {
      const temp = a(b(...args));
      return temp;
    };
  });
}

const composedFunction = compose(f2, f3, f4, f5);
const result = composedFunction(2);

在第一次reduce 迭代中,累加器是f2,所以我们将得到f2(f3(2))=12。在下一次迭代中,我们将调用f4(12)=48。在最后一次迭代中,我们将调用f5(48)=240。所以评估顺序是f5(f4(f2(f3(2))))。但是使用console.log,我看到评估顺序是f2(f3(f4(f5(2)))),巧合的是240。

据我了解,函数 y 被所有数组元素调用,那么为什么只有最后一个函数将 2 作为参数?

【问题讨论】:

  • compose 的错误实现。最重要的是 1) 它不是一个完整的函数。使用x => x(身份函数)作为初始累加器,reduce 的第二个参数,2)支持最里面的函数的多个参数是愚蠢和昂贵的 - 你只需要支持一元函数组合,以及 3)temp毫无意义,您立即跳过变量和return。即const comp = (f,g) => x => f(g(x)); const compose = (...fs) => fs.reduce(comp, x => x)

标签: javascript functional-programming evaluation


【解决方案1】:

让我们通过一个非常简单的示例来逐步完成代码:

 compose(f2, f3, f4)

由于没有将初始值传递给 reduce,它将从数组的第一个 (f2) 和第二个 (f3) 值开始,并以此调用回调,x 被调用,a 是 @987654324 @ 和 bf3。现在x 什么都不做,它只是返回函数y,它可以通过闭包访问a 和b。

Reduce 现在将继续到第三个元素,第一个参数是前一个回调的结果(关闭的y),第二个参数是f4。现在再次调用x,并在y 上创建另一个闭包,y 得到整个函数的 finally 返回。

如果我们尝试可视化这样的封闭函数,它将是:

 y { // closure of y
  a -> y { // a references another closure of y
    a -> f3,
    b -> f2
  },
  b -> f4
}

现在你调用闭包 y 并将 2 传递给它,它将调用 b (f4) 并将结果传递给调用 a (闭包 y)。

 a         (  b(...args))   
 y { ... } (  f4(2) )

现在关闭的 y 也会这样做:

 a (  b ( ...args))
 f2( f3( f4( 2 ) ) )

提示: 有时很难跟踪封闭的值,因此控制台为您提供了很好的实用程序来跟踪它们:在控制台的“调试器”选项卡中打开您的代码,单击在函数调用要附加断点的行号上,然后再次运行代码,只要到达断点,执行就会产生,您可以看到所有变量的值(包括闭包变量)。

【讨论】:

    【解决方案2】:

    reduce 不是调用函数 f2、f3、f3、f5,而是从这些函数创建一个函数。这是每次迭代中累加器的值。请注意,该值是function,而不是函数执行的结果。

    1:a=f2;b=f3;返回值(NOT TEMP but function y)=f2(f3(...args))

    2:a(prev return value)=f2(f3(...args));b=f4;return value=f2(f3(f4(...args)))

    等等……

    【讨论】:

      【解决方案3】:

      compose函数可以改写为:

      function compose(...funcs) {
        return funcs.reduce(function (a, b) {
          return function (arg) {
            const temp = a(b(arg));
            return temp;
          };
        });
      }
      

      第一次迭代后,作为下一个累加器传入的返回函数为:

      function (arg) {       // R1
        return f2(f3(arg));
      

      }

      第二次迭代后,作为下一个累加器传入的返回函数为:

      function (arg) {       // R2
        return R1(f4(arg));
      

      }

      最后,分配给composedFunction的返回函数是:

      function (arg) {       // composedFunction
        return R2(f5(arg));
      

      }

      所以运行composedFunction(2) 并返回链条:

      f5(2) returns 10
      
      R2(10) returns R1(f4(10))
      which is       R1(40)
      
      R1(40) returns f2(f3(40))
      which is   f2(120)
      which is   240
      

      希望这就足够了。

      它可以写成一个单独的调用:

      function composedFunction(arg) {
        return f2(f3(f4(f5(arg))));
      }
      

      【讨论】:

        猜你喜欢
        • 2018-10-19
        • 2018-12-16
        • 2020-03-26
        • 2012-01-07
        • 2021-06-20
        • 1970-01-01
        • 2017-02-02
        • 2016-08-08
        相关资源
        最近更新 更多