Compose - 函数式编程

Compose - functional programming

我想创建自己的 compose 实现

这个有效

const compose = (...fns) => fns.reduce((f,g) => (...args) => f(g(...args)));

这不是

const compose1 = (...fns) => (...args) => fns.reduce((f,g) => f(g(...args)));

.

const multi = a => x => x*a;
const devide = a => x => x/a;
const add = a => x => x+a;

compose(add(6), multi(4), devide(5))(5); // 10 as expected

compose1(add(6), multi(4), devide(5))(5); // Uncaught TypeError: f is not a function

你能解释一下为什么第二个实现不起作用吗?

通过将语法扩展为多行,您可以开始查看第二个函数中的问题所在。您正在调用 fns.reduce,而您应该调用 args.reduce。从那里开始,它仍然 returns 一个不同的值,所以很可能还有其他一些问题,但这只是一个开始。

第二个控制台日志还应显示 f 的值不是您期望的值。

理想情况下,您应该只为非常简单的操作编写 shorthand,或者为您已经以更易于阅读和测试的方式进行全面测试的内容编写。

const compose = (...fns) => {
  return fns.reduce((f,g) => {
    console.log('f: ',f,'g: ',g)
    return (...args) => f(g(...args))
  })
 };

const compose1 = (...fns) => {
  return (...args) => {
    return args.reduce((f,g) => { //was fns.reduce
      console.log('f: ',f,'g: ',g)
      return f(g(...args))
    })
  }
}

const multi = a => x => x*a;
const devide = a => x => x/a;
const add = a => x => x+a;

var result1 = compose(add(6), multi(4), devide(5))(5); // 10 as expected
console.log(result1)
var result2 = compose1(add(6), multi(4), devide(5))(5); // Uncaught TypeError: f is not a function
console.log(result2)

要使您的版本正常运行,您需要执行以下操作:

const compose1 = (...fns) => (...args) => fns.reduceRight((res, f) => f(res), fns.pop()(...args))

你失败的原因是因为reduce(和reduceRight)接受了一个带有这个签名的函数:

(previousValue, currentValue) => newPreviousValue

解释:

在你的版本中发生的事情是,在第一次 reduce 调用你的函数后,newPreviousValue 现在不再是一个函数,它现在是 f(g(...args))(最后一个函数调用的结果), 因此当 reduce 再次尝试 "call" 那个函数时,它使用上次调用的结果,就好像它是一个函数一样,所以你会得到错误。

如果你看到我上面的那个,res就是previousValue,这是最后一个函数调用的结果,但它没有被用作函数。

进一步

如果你想知道为什么它在第一次调用时就成功了,那么这是因为你没有为 reduce 提供初始值,所以它只是从列表作为函数的参数,它允许您执行:f(g(...args))

const compose = (...fns) => {
  console.log('function', fns);
  return fns.reduce((f,g) => {
    console.log('f and g' , f,g)
    return (...args) => {
      console.log('args is ' , args)
      return f(g(...args));
    }
  })
};

// call with 
compose(add(6), multi(4), devide(5))(5);

将产生

VM1239:2 function (3) [ƒ, ƒ, ƒ]
VM1239:4 f and g x => x+a x => x*a
VM1239:4 f and g (...args) => {
      console.log('args is ' , args)
      return f(g(...args));
    } x => x/a
VM1239:6 args is  [5]
VM1239:6 args is  [1]
10

一切正常 对你的 compose1

做同样的事情
const compose1 = (...fns) => {
  console.log('functions ', fns)
  return (...args) => {
    console.log('args', args)
    return fns.reduce((f,g) => {
      console.log('f and g', f,g)
      return f(g(...args))
    })
  }
};
//called with 
compose1(add(6), multi(4), devide(5))(5)

将产生

VM1783:2 functions  (3) [ƒ, ƒ, ƒ]
 0: x => x+a
 1: x => x*a
 2: x => x/a
VM1783:4 args [5]
VM1783:6 f and g x => x+a x => x*a
VM1783:6 f and g 26 x => x/a
VM1783:7 Uncaught TypeError: f is not a function
    at fns.reduce (<anonymous>:7:14)
    at Array.reduce (<anonymous>)
    at args (<anonymous>:5:16)
    at <anonymous>:1:38

所以你的 f 现在是 26,这不是一个函数:)

解释:

you called 
f (g (...args)) 
f ( g ( 5 ) )
f ( 5 * a)  // a is 4
f ( 20 )
16 + a //  a is 6
26

让我们通过分解来看看你的compose1

const compose1 = (...fns) => {
    // Returns a function that reduces fns each time it is called        
    return (...args) => {
        // Returns the reduction of fns
        return fns.reduce((f,g) => {
            // Returns the result of calling f(g(...args))
            return f(g(...args))
        })
    }
};

现在让我们看一下这个版本的 compose 的示例调用:

compose1(add(6), multi(4), devide(5))(5);

首先,我们调用compose1。这个returns这个函数:

(...args) => {
    // Returns the reduction of fns
    return fns.reduce((f,g) => {
        // Returns the result of calling f(g(...args))
        return f(g(...args))
    })
}

然后我们用参数5调用这个函数,本质上就是像之前返回的函数一样调用fns.reduce。要查看到底出了什么问题,让我们 运行 通过 reduce 的几次迭代:

第一次迭代:

fns.reduce((f = add(6), g = multi(4)) => {
    // Returns the result of calling f(g(...args))
    return f(g(...args = [5]))
    // g is `multi(4)`, so do `multi(4)(5)`, which returns 20. Next:
    // f is `add(6)`, so do `add(6)(20)`, which returns 26.
    // Remember that the return from last iteration is
    //   the first parameter of the next iteration
})

第二次迭代:

fns.reduce((f = 26, g = devide(5)) => {
    // Returns the result of calling f(g(...args))
    return f(g(...args = [5]))
    // g is `devide(5)`, so do `devide(5)(5)`, which returns 1. Next:
    // f is 26, so do `26(1)`. However, 26 is not a function.
    // You get the error `f is not a function`.
})