我们来分析一下改进后的compose做了什么
compose = (fns) =>
(arg, ...restArgs) =>
fns.reduceRight((acc, func) => func(acc, ...restArgs), arg);
当您为compose 提供多个函数时,您会返回...一个函数。在你的情况下,你给它一个名字,fn。
fn 函数是什么样的?通过简单的替换,您可以将其视为:
(arg, ...restArgs) => fns.reduceRight((acc, func) => func(acc, ...restArgs), arg);
fns === [(x) => x - 8, (x) => x ** 2, (x, y) => (y > 0 ? x + 3 : x - 3)].
所以你可以给这个函数fn 提供一些参数,这些参数将与(arg, ...restArgs) 进行“模式匹配”;在您的示例中,当您调用 fn("3", 1) 时,arg 是 "3" 和 restArgs 是 [1] (所以 ...restArgs 在逗号后扩展为 1,所以你看到 fn("3", 1) 减少到
fns.reduceRight((acc, func) => func(acc, 1), "3");
由此可见
- 最右边的函数
(x, y) => (y > 0 ? x + 3 : x - 3)被调用,带有两个参数"3"(acc的初始值)和1,
- 结果将作为第一个参数传递给中间函数,然后调用
func,
- 等等,
但重点是func的第二个参数,即1,只被最右边的函数使用,而它被传递给但被其他两个函数忽略!
结论
函数组合是一元函数之间的事情。将其与元数大于 1 的函数一起使用会导致混淆。
例如考虑这两个函数
square = (x) => x**2; // unary
plus = (x,y) => x + y; // binary
你能编曲吗?好吧,你可以把它们组合成这样的函数
sum_and_square = (x,y) => square(plus(x,y));
问题底部的compose 功能会很顺利:
sum_and_square = compose([square, plus]);
但是如果你的两个函数是这些呢?
apply_twice = (f) => ((x) => f(f(x))); // still unary, technically
plus = (x,y) => x + y; // still binary
您的compose 不起作用。
即使,如果函数 plus 被柯里化了,例如如果它被定义为
plus = (x) => (y) => x + y
然后可以考虑将它们组合成一个函数,如下所示:
f = (x,y) => apply_twice(plus(x))(y)
这会产生f(3,4) === 10。
您可以通过f = compose([apply_twice, plus]) 获取它。
外观改进
此外,我建议进行“化妆品”更改:让compose 接受...fns 而不是fns,
compose = (...fns)/* I've only added the three dots on this line */ =>
(arg, ...restArgs) =>
fns.reduceRight((acc, func) => func(acc, ...restArgs), arg);
并且您可以在没有 groupint 的情况下调用它,将函数组合成一个数组,例如你应该写 compose(apply_twice, plus) 而不是 compose([apply_twice, plus])。
该库中有两个函数可以处理函数组合: