JavaScript和ES6中curried函数的函数应用程序

JavaScript和ES6中curried函数的函数应用程序,javascript,functional-programming,ecmascript-6,apply,currying,Javascript,Functional Programming,Ecmascript 6,Apply,Currying,我喜欢ECMAScript 6允许您编写如下的curried函数: var add=x=>y=>z=>x+y+z; 但是,我讨厌我们需要在curried函数的每个参数中插入括号: const uncurry = fn => (...args) => args.reduce((f, x) => f(x), fn); // or alternatively: const uncurry = fn => (...args) => { let f = fn;

我喜欢ECMAScript 6允许您编写如下的curried函数:

var add=x=>y=>z=>x+y+z; 但是,我讨厌我们需要在curried函数的每个参数中插入括号:

const uncurry = fn => (...args) => args.reduce((f, x) => f(x), fn);

// or alternatively:
const uncurry = fn => (...args) => {
    let f = fn;
    for (const x of args) f = f(x);
    return f;
}
const $ = (func, ...args) => args.reduce((f, x) => f(x), func);
const sum = x => y => z => x + y + z;

$(sum, 1, 2, 3); // 6
$(sum, 1, 2)(3); // 6
$(sum, 1)(2, 3); // z => x + y + z
add235; 我希望能够同时将curried函数应用于多个参数:

增补2,3,5;
我该怎么办?我不在乎性能。

大多数人编写的curry函数如下:

var add=电流函数x,y,z{ 返回x+y+z; }; 增补2,3,5; 主要是因为他们不想写这个:

var add=函数x{ 返回函数y{ 返回函数z{ 返回x+y+z; }; }; }; add235; 然而

然后,ECMAScript 6为我们解决了第一个问题:

var add=x=>y=>z=>x+y+z; 但是,我们仍然需要自己解决第二个问题:

add235; 现在是我们解决这个问题的时候了:

var$=func,…args=>args.reducef,x=>fx,func; 我希望您喜欢Lisp语法:

$add,2,3,5; 对不起,jQuery。函数应用更为基础

另外,Bergi's也很棒:

const uncurry=func=>…args=>{ var结果=func; 对于args中的let arg 结果=结果目标; 返回结果; } var add=uncurryx=>y=>z=>x+y+z; 增补2,3,5;
但是,我仍然更喜欢使用$。

大多数人编写的咖喱函数如下:

var add=电流函数x,y,z{ 返回x+y+z; }; 增补2,3,5; 主要是因为他们不想写这个:

var add=函数x{ 返回函数y{ 返回函数z{ 返回x+y+z; }; }; }; add235; 然而

然后,ECMAScript 6为我们解决了第一个问题:

var add=x=>y=>z=>x+y+z; 但是,我们仍然需要自己解决第二个问题:

add235; 现在是我们解决这个问题的时候了:

var$=func,…args=>args.reducef,x=>fx,func; 我希望您喜欢Lisp语法:

$add,2,3,5; 对不起,jQuery。函数应用更为基础

另外,Bergi's也很棒:

const uncurry=func=>…args=>{ var结果=func; 对于args中的let arg 结果=结果目标; 返回结果; } var add=uncurryx=>y=>z=>x+y+z; 增补2,3,5;
但是,我仍然更喜欢使用$。

您可以轻松编写一个函数,将多个参数应用于此类常用函数:

const uncurry = fn => (...args) => args.reduce((f, x) => f(x), fn);

// or alternatively:
const uncurry = fn => (...args) => {
    let f = fn;
    for (const x of args) f = f(x);
    return f;
}
const $ = (func, ...args) => args.reduce((f, x) => f(x), func);
const sum = x => y => z => x + y + z;

$(sum, 1, 2, 3); // 6
$(sum, 1, 2)(3); // 6
$(sum, 1)(2, 3); // z => x + y + z
现在可以像这样调用add:

如果你仍然讨厌它,你也可以用它

const $ = uncurry(uncurry);

$(add, 2, 3, 4)

您可以轻松编写一个函数,将多个参数应用于这样一个curried函数:

const uncurry = fn => (...args) => args.reduce((f, x) => f(x), fn);

// or alternatively:
const uncurry = fn => (...args) => {
    let f = fn;
    for (const x of args) f = f(x);
    return f;
}
const $ = (func, ...args) => args.reduce((f, x) => f(x), func);
const sum = x => y => z => x + y + z;

$(sum, 1, 2, 3); // 6
$(sum, 1, 2)(3); // 6
$(sum, 1)(2, 3); // z => x + y + z
现在可以像这样调用add:

如果你仍然讨厌它,你也可以用它

const $ = uncurry(uncurry);

$(add, 2, 3, 4)

咖喱和咖喱函数的应用在Javascript中是有争议的问题。简单地说,有两种相反的观点,我简要地说明了这两种观点

-仅在必要时使用单独的咖喱功能

从原则上讲,从其他语言或范例中改编概念是一件好事。不过,这种改编应该用目标语言的基本手段来完成。这对javascript中的咖喱意味着什么

curried函数作为一元函数序列调用:add3123;//6. 自己的函数用箭头const add3=x=>y=>z=>x+y+z手动转换; 第三方函数或方法由单独的curry函数实现 -默认情况下使用单独的curry实现

建议的$/uncurry函数存在问题:

const uncurry = fn => (...args) => args.reduce((f, x) => f(x), fn);

// or alternatively:
const uncurry = fn => (...args) => {
    let f = fn;
    for (const x of args) f = f(x);
    return f;
}
const $ = (func, ...args) => args.reduce((f, x) => f(x), func);
const sum = x => y => z => x + y + z;

$(sum, 1, 2, 3); // 6
$(sum, 1, 2)(3); // 6
$(sum, 1)(2, 3); // z => x + y + z
以这种方式,未载波函数只能使用无限数量的参数应用一次。任何后续调用都必须是一元调用。函数完全按照它所承诺的那样执行。但是,它不允许应用curried函数,例如JavaScript开发人员习惯使用的函数。当前大多数curry实现都更加灵活。下面是一个扩展的实现:

const uncurry = f => (...args) => args.reduce(
  (g, x) => (g = g(x), typeof g === "function" && g.length === 1
   ? uncurry(g) 
   : g), f
);

const sum = uncurry(x => y => z => x + y + z);

sum(1, 2, 3); // 6
sum(1, 2)(3); // 6
sum(1)(2, 3); // 6
如果您喜欢自动取消终止,则此实现可以工作:一旦未终止函数本身生成一个curried函数作为返回值,此返回函数将自动取消终止。如果您更喜欢控制,那么下面的实现可能更合适

最终未经批准的实施

幸运的是,我们能够重用partial并保持curryN简洁。使用此解决方案,还可以实现可变函数或具有可选参数的函数

奖励:功能化和咖喱化方法

要使用curry方法,我们需要在显式参数中转换这个讨厌的隐式属性。事实证明,我们可以再次重用partial来实现适当的实现:

const apply = uncurry(2)(arity => key => {
  return arity
   ? partial(arity + 1)(args => args[arity][key](...args.slice(0, arity)))
   : o => o[key]();
});

apply(0, "toLowerCase")("A|B|C"); // "a|b|c"
apply(0, "toLowerCase", "A|B|C"); // "a|b|c"

apply(1, "split")("|")("A|B|C"); // ["A", "B", "C"]
apply(1, "split")("|", "A|B|C"); // ["A", "B", "C"]
apply(1, "split", "|", "A|B|C"); // ["A", "B", "C"]

apply(2, "includes")("A")(0)("A|B|C"); // true
apply(2, "includes", "A", 0, "A|B|C"); // true

在本书中,我们将详细讨论咖喱。咖喱和咖喱函数的应用在Javascript中是有争议的问题。简单地说,有两种相反的观点,我简要地说明了这两种观点

-仅在必要时使用单独的咖喱功能 多余的

从原则上讲,从其他语言或范例中改编概念是一件好事。不过,这种改编应该用目标语言的基本手段来完成。这对javascript中的咖喱意味着什么

curried函数作为一元函数序列调用:add3123;//6. 自己的函数用箭头const add3=x=>y=>z=>x+y+z手动转换; 第三方函数或方法由单独的curry函数实现 -默认情况下使用单独的curry实现

建议的$/uncurry函数存在问题:

const uncurry = fn => (...args) => args.reduce((f, x) => f(x), fn);

// or alternatively:
const uncurry = fn => (...args) => {
    let f = fn;
    for (const x of args) f = f(x);
    return f;
}
const $ = (func, ...args) => args.reduce((f, x) => f(x), func);
const sum = x => y => z => x + y + z;

$(sum, 1, 2, 3); // 6
$(sum, 1, 2)(3); // 6
$(sum, 1)(2, 3); // z => x + y + z
以这种方式,未载波函数只能使用无限数量的参数应用一次。任何后续调用都必须是一元调用。函数完全按照它所承诺的那样执行。但是,它不允许应用curried函数,例如JavaScript开发人员习惯使用的函数。当前大多数curry实现都更加灵活。下面是一个扩展的实现:

const uncurry = f => (...args) => args.reduce(
  (g, x) => (g = g(x), typeof g === "function" && g.length === 1
   ? uncurry(g) 
   : g), f
);

const sum = uncurry(x => y => z => x + y + z);

sum(1, 2, 3); // 6
sum(1, 2)(3); // 6
sum(1)(2, 3); // 6
如果您喜欢自动取消终止,则此实现可以工作:一旦未终止函数本身生成一个curried函数作为返回值,此返回函数将自动取消终止。如果您更喜欢控制,那么下面的实现可能更合适

最终未经批准的实施

幸运的是,我们能够重用partial并保持curryN简洁。使用此解决方案,还可以实现可变函数或具有可选参数的函数

奖励:功能化和咖喱化方法

要使用curry方法,我们需要在显式参数中转换这个讨厌的隐式属性。事实证明,我们可以再次重用partial来实现适当的实现:

const apply = uncurry(2)(arity => key => {
  return arity
   ? partial(arity + 1)(args => args[arity][key](...args.slice(0, arity)))
   : o => o[key]();
});

apply(0, "toLowerCase")("A|B|C"); // "a|b|c"
apply(0, "toLowerCase", "A|B|C"); // "a|b|c"

apply(1, "split")("|")("A|B|C"); // ["A", "B", "C"]
apply(1, "split")("|", "A|B|C"); // ["A", "B", "C"]
apply(1, "split", "|", "A|B|C"); // ["A", "B", "C"]

apply(2, "includes")("A")(0)("A|B|C"); // true
apply(2, "includes", "A", 0, "A|B|C"); // true

在这篇文章中,咖喱是被详细讨论的。

如果你能给我一个哈斯克尔风格的加2、3、5美元,我会很高兴:-ES6中的参数,真的吗?@Bergi将我的解决方案升级到ES6。add123不是问题,ES6当然不能解决或消除咖喱的需要,它只允许你更轻松地以咖喱形式编写新函数。对于尚未使用curry的现有函数,curry过程仍然是必需的。@naomik也许您不介意编写add123,这对您有好处,但并非每个人都同意您的观点。你当然不应该把你的观点强加给别人。而且,我觉得你是一个完全废除咖喱的支持者。我想我错了。现在如果你能给我一个Haskell风格的add$2、3、5,我会很高兴:-ES6中的参数,真的吗?@Bergi将我的解决方案升级到ES6。add123不是问题,ES6当然不能解决或消除咖喱的需要,它只允许你更轻松地以咖喱形式编写新函数。对于尚未使用curry的现有函数,curry过程仍然是必需的。@naomik也许您不介意编写add123,这对您有好处,但并非每个人都同意您的观点。你当然不应该把你的观点强加给别人。而且,我觉得你是一个完全废除咖喱的支持者。我想我错了。把它改名为uncurry是明智的。它在哈斯克尔也有效。看看这些类型,嗯,肯定有用。但它不会将所有参数传递给内部应用程序,只传递一个参数,其余的由外部应用程序完成。在Haskell中,它变成func,a,b而不是func,a,b,这就是我所困惑的about@AaditMShah:谢谢,修好了。在处理咖喱函数时,我们真的希望。Hrmpf。@Bergi是一个利用自身功能的绝佳演示:applyapply@geoffrey如果我们执行const normalAdd=uncurrycurriedAdd,然后调用normalAdd1,2,3和normalAdd4,5,6,如果我们在第一个调用中修改了fn,那么第二个调用将不起作用。将apply重命名为uncurry是明智的。它在哈斯克尔也有效。看看这些类型,嗯,肯定有用。但它不会将所有参数传递给内部应用程序,只传递一个参数,其余的由外部应用程序完成。在Haskell中,它变成func,a,b而不是func,a,b,这就是我所困惑的about@AaditMShah:谢谢,修好了。在处理咖喱函数时,我们真的希望。Hrmpf。@Bergi是一个利用自身功能的绝佳演示:applyapply@geoffrey如果我们执行const normalAdd=uncurrycurriedAdd,然后调用normalAdd1,2,3和normalAdd4,5,6,那么如果我们在第一个调用中修改了fn,那么第二个调用将不起作用。这样,使用无限数量的参数只能应用一次uncurried函数。任何后续调用都必须是一元调用。这不是很地道。你没抓住重点。如果你打算写$sum,1,23或$sum,12,3,那么你就完全违背了拥有$sum的目的。你不妨写一封信。事实上,$函数对于Lisp程序员来说是惯用的。您可以编写$$sum,1,2,3,甚至只是$sum,1,2,3,而不是$sum,12,3。你的反例根本无法令人信服。有严重的反例吗?我的$函数不是
魔术这是一种持续使用的肉。您不能将$与普通函数应用程序混合使用,而期望一切正常。写$sum,12,3就像油和水的混合。使用$或使用普通函数应用程序,但不能同时使用两者。我写$的动机是简单和快速。$function比curry更简单,比uncurry实现更快。您所要做的就是始终如一地使用它,这样您就能够使用Lispy语法编写JavaScript函数程序,并具有currying所能提供的所有好处。@Aadit:当我们谈论JavaScript中的Curryd函数时,很多人会联想到以下应用程序:fx,y,z,fxy,z,fx,yz,…-即使这不再是数学意义上的curried函数的应用。您自己的实现就是这样做的。因此,$为什么放弃这种行为是一个完全合理的问题。您的实现不符合我的需要,我倾向于括号语法。OP的问题是JavaScript和ES6中curried函数的函数应用程序。这正是我正在做的:利用uncurry来实现人们在JavaScript中习惯的咖喱函数应用程序。现在的问题似乎是如何在Lisp风格中应用curried函数?。所以是的,我想我错过了真正的问题!符合事实的你确实回答了这个问题+1美元。事实上,我想得越多,你的答案就越有意义。我认为你的答案更值得打勾,尽管说实话,我不同意你答案的第一部分,即$is magic。这样,未配对函数只能应用一次,且参数数量不限。任何后续调用都必须是一元调用。这不是很地道。你没抓住重点。如果你打算写$sum,1,23或$sum,12,3,那么你就完全违背了拥有$sum的目的。你不妨写一封信。事实上,$函数对于Lisp程序员来说是惯用的。您可以编写$$sum,1,2,3,甚至只是$sum,1,2,3,而不是$sum,12,3。你的反例根本无法令人信服。有严肃的反例吗?我的$函数不是魔法。这是一种持续使用的肉。您不能将$与普通函数应用程序混合使用,而期望一切正常。写$sum,12,3就像油和水的混合。使用$或使用普通函数应用程序,但不能同时使用两者。我写$的动机是简单和快速。$function比curry更简单,比uncurry实现更快。您所要做的就是始终如一地使用它,这样您就能够使用Lispy语法编写JavaScript函数程序,并具有currying所能提供的所有好处。@Aadit:当我们谈论JavaScript中的Curryd函数时,很多人会联想到以下应用程序:fx,y,z,fxy,z,fx,yz,…-即使这不再是数学意义上的curried函数的应用。您自己的实现就是这样做的。因此,$为什么放弃这种行为是一个完全合理的问题。您的实现不符合我的需要,我倾向于括号语法。OP的问题是JavaScript和ES6中curried函数的函数应用程序。这正是我正在做的:利用uncurry来实现人们在JavaScript中习惯的咖喱函数应用程序。现在的问题似乎是如何在Lisp风格中应用curried函数?。所以是的,我想我错过了真正的问题!符合事实的你确实回答了这个问题+1美元。事实上,我想得越多,你的答案就越有意义。我认为你的答案更值得打勾,尽管说实话,我不同意你答案的第一部分,即那美元是神奇的。