JavaScript 和 ES6 中柯里化函数的函数应用 [英] Function application for curried functions in JavaScript and ES6

查看:20
本文介绍了JavaScript 和 ES6 中柯里化函数的函数应用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我喜欢 ECMAScript 6 允许您编写这样的柯里化函数:

I love that ECMAScript 6 allows you to write curried functions like this:

var add = x => y => z => x + y + z;

然而,我讨厌我们需要为柯里化函数的每个参数加上括号:

However, I hate that we need to parenthesize every argument of a curried function:

add(2)(3)(5);

我希望能够一次将柯里化函数应用于多个参数:

I want to be able to apply curried functions to multiple arguments at once:

add(2, 3, 5);

我该怎么办?我不在乎性能.

What should I do? I don't care about performance.

推荐答案

柯里化和柯里化函数的应用在 Javascript 中是有争议的问题.简单来说,有两种相反的观点,我简单地说明一下.

Currying and the application of curried functions are controversial issues in Javascript. In simple terms, there are two opposing views, which I illustrate both briefly.

- 仅在必要时使用单独的咖喱函数

对来自其他语言或范式的概念进行改编,原则上是一件好事.但是,这种改编应该使用目标语言的基本手段来完成.这对于 javascript 中的柯里化意味着什么?

The adaptation of concepts from other languages or paradigms is in principle a good thing. This adaptation though, should be done with the elementary means of the target language. What does that mean for currying in javascript?

  • 柯里化函数被称为一元函数序列:add3(1)(2)(3);//6
  • 自己的函数用箭头手动柯里化 const add3 = x =>y =>z=>x + y + z;
  • 第三方函数或方法由单独的柯里化函数柯里化

- 默认使用单独的 curry 实现

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

There's a problem with the proposed $/uncurry function:

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

通过这种方式,非柯里化函数只能以无限数量的参数应用一次.任何后续调用都必须是一元的.该函数完全符合它的承诺.但是,它不允许应用柯里化函数,如 JavaScript 开发人员所习惯的.大多数当前的 curry 实现都更加灵活.这是一个扩展的实现:

In this way uncurried functions can only once be applied with an unlimited number of arguments. Any subsequent calls must be made unary. The function does exactly what it promises. However, it does not allow application of curried functions, such as JavaScript developers are used to. Most of the current curry implementations are more flexible. Here's an extended implementation:

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

这个实现有效,如果你喜欢自动反柯里化:一旦一个非柯里化函数本身产生一个柯里化函数作为返回值,这个返回的函数就会自动取消柯里化.如果您更喜欢更多控制,以下实现可能更合适.

This implementation works, if you like auto-uncurrying: Once a uncurried function itself produces a curried function as a return value, this returned function is automatically uncurried. If you prefer more control, the following implementation might be more appropriate.

最终的 uncurry 实现

const partial = arity => f => function _(...args) {
  return args.length < arity
   ? (...args_) => _(...args.concat(args_))
   : f(args);
};

const uncurry = arity => f => partial(arity)(args => args.reduce((g, x) => g(x), f));
const sum = uncurry(3)(x => y => z => x + y + z);

sum(1, 2, 3); // 6
sum(1, 2)(3); // 6
sum(1)(2, 3); // 6

这个微小的 arity 参数为我们带来了所需的控制.我觉得值得.

This tiny arity parameter brings us the desired control. I think it's worth it.

剩下的咖喱解决方案

如果函数超出我们的控制范围,因此没有被手动柯里化,我们该怎么办?

What do we do with functions that are beyond our control and hence haven't been manually curried?

const curryN = uncurry(2)(arity => f => partial(arity)(args => f(...args)));
const add = curryN(2, (x, y) => x + y);
const add2 = add(2);

add2(4); // 6

幸运的是,我们能够重用 partial 并保持 curryN 简洁.使用此解决方案,还可以对可变参数函数或带有可选参数的函数进行柯里化.

Fortunately, we were able to reuse partial and keep curryN concise. With this solution also variadic functions or such with optional parameters can be curried.

奖励:功能化"和柯里化方法

要柯里化方法,我们需要在显式参数中转换这个讨厌的、隐式的 this 属性.事实证明,我们可以再次重用 partial 以获得适当的实现:

To curry methods, we need to transform this nasty, implicit this property in an explicit parameter. It turns out that we can reuse partial for an adequate implementation once again:

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

在这篇博文中详细讨论了柯里化.

In this blog post currying is discussed in detail.

这篇关于JavaScript 和 ES6 中柯里化函数的函数应用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆