组合嵌套函数的更好方法

问题描述

假设我有两个具有以下类型的函数

f :: (a,b) -> c
g :: (a,b,c) -> d

我可以按如下方式组合它们。

function h(a,b) {
    const c = f(a,b);
    const d = g(a,c);
    return d;
}

这里,hgf 的组合。然而,这看起来很像带有常量声明和返回语句的命令式代码。如何以函数式风格组合任意两个这样的函数

解决方法

您可以在一行中定义 h,如下所示。

const h = (a,b) => g(a,b,f(a,b));

然后你可以将这个组合概括为所有这样的函数,如下所示。

const ss = (g,f) => (a,b));

const h = ss(g,f);

这实际上与 S combinator 相同,但扩展到两个输入。因此,名称 ss

或者,我们可以使用 Scott Sauyet 建议的扩展运算符将 S 组合子推广到任意数量的输入。

const s = (g,f) => (...args) => g(...args,f(...args));

const h = s(g,f);

就我个人而言,我会远离多变量函数。

顺便说一句,即使使用常量声明,您的原始示例也不是强制性的。

,

这看起来像是一个如何以与简单组合不同的方式组合两个函数的问题。我们可以编写组合器来帮我们做到这一点。

在这种情况下,我们本质上希望使用 a 中的初始 bf 参数来生成 d 并使用 a、{{1 }} 和 b 中的 d。我们可以编写一个结合任意 gf 的函数来做到这一点,保留我们的原始参数并在对 g 的调用中添加一个额外的参数。也许我们会称它为g

addArg

这个版本试图更通用一些;我们不只处理两个参数。我们可以为 const addArg = (g,f) => (...args) => g (...args,f (...args)) 设置任意数量的参数,并在对 f 的调用中再添加一个。

这是一个快速演示:

g

,

我认为之前的答案没有任何问题,但我想添加一个替代方案,并警告您有关组合器的信息。

我对使用自定义组合符感到不舒服,因为读者必须查阅它们的定义,这降低了使用它们的价值。

我熟悉 lift2 并且我的第一直觉是弯曲它以将其应用到您的模式中(您的函数必须被柯里化才能工作)

const lift2 = f => g => h => x => f (g (x)) (h (x));
const id = x => x;

const h = (a,b) => lift2 (g (a)) (id) (f (a)) (b);

因此,我们将 fg 部分应用于 a,并使用 id 存储 b,直到它被馈送到 g

如果您在解析上述内容时遇到问题,我们可以这样表达 lift2,以便更清楚地说明发生了什么:

const lift2 = ga => id => fa => b => ga (id (b)) (fa (b));

老实说,我会选择 Aadit M Shah 建议的第一个单线,或者选择第二个,但名为 h,以表达“这只是您期望的 h,但是注入了依赖项”...或您的实现。您的甚至提供了解释性变量名称。我的意思是为什么不!

即使是已知的组合器有时也只会混淆意图。这完全取决于你和谁一起工作。这包括你未来的自己 ;)