compose 函数如何处理多个参数?

Posted

技术标签:

【中文标题】compose 函数如何处理多个参数?【英文标题】:How does compose function work with multiple parameters? 【发布时间】:2021-11-16 16:19:32 【问题描述】:

这是我需要改进的“撰写”功能:

const compose = (fns) => (...args) => fns.reduceRight((args, fn) => [fn(...args)], args)[0];

这是一个实际的实现:

const compose = (fns) => (...args) => fns.reduceRight((args, fn) => [fn(...args)], args)[0];

const fn = compose([
  (x) => x - 8,
  (x) => x ** 2,
  (x, y) => (y > 0 ? x + 3 : x - 3),
]);

console.log(fn("3", 1)); // 1081
console.log(fn("3", -1)); // -8

这是我的导师提出的一项改进。

const compose = (fns) => (arg, ...restArgs) => fns.reduceRight((acc, func) => func(acc, ...restArgs), arg);

如果我们在第一次迭代时传递 func(x, [y]) 之类的参数列表,我仍然不明白我们如何使函数与 [y] 的解压缩数组一起工作?

【问题讨论】:

【参考方案1】:

我们来分析一下改进后的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])

顺便说一句,有lodash

该库中有两个函数可以处理函数组合:

_.flow _.flowRight,在lodash/fp中别名为_.compose

【讨论】:

感谢您的回答。我不太明白的一件事是“...restArgs 如何在逗号后扩展为 1”?第二个值是如何从数组中解压出来的? 我不是专家,但关键是如果一个函数有一个参数写成...restArgs,那么对应的参数 restArgs 将是所有这些参数的数组;当您在函数体中写入...restArgs 时,您将该数组扩展为一个逗号分隔的东西。要查看差异,请在提示符处输入 ((...x) => [...x, x])(1,2,3) 并检查结果。

以上是关于compose 函数如何处理多个参数?的主要内容,如果未能解决你的问题,请参考以下文章

Jetpack Compose 和 Compose Navigation 如何处理 Android 活动?

django-rest-framework 如何处理多个 URL 参数?

如何处理 Jetpack Compose 中的导航?

FastAPI 学习之路请求体有多个参数如何处理?

HttpWebRequest发送post请求时有多个参数如何处理

Python 函数如何处理你传入的参数类型?