Ramda 中的 curry 和偏函数有什么区别?
What is the different between curry and partial function in Ramda?
curry and partial 函数似乎做同样的事情。 (也许唯一的区别是参数的数量)
这只是方便的问题,还是有充分的理由让两个函数做类似的事情。
正如您简要提到的,curry
函数可以接受一个带有 n 个参数的函数,return n 个带有一个参数的函数。柯里化是将函数组合成高阶函数的重要工具。
函数的部分应用是柯里化的一种。事实上,如果你查看 Ramda source code,你会发现 partial
函数是使用 curry 函数实现的。
var _arity = require('./_arity');
var _curry2 = require('./_curry2');
module.exports = function _createPartialApplicator(concat) {
return _curry2(function(fn, args) {
return _arity(Math.max(0, fn.length - args.length), function() {
return fn.apply(this, concat(args, arguments));
});
});
};
此外,请查看 this question 从根本上解释差异。
来自更广泛的 FP 社区的许多答案可能会误导您。 Ramda 的柯里化在我看来似乎将 ML 风格语言的柯里化精神带入 Javascript,但并不完全相同。
部分应用程序在 Ramda 中可能是相当标准的。 (免责声明:我是 Ramda 的作者之一。)它也更容易描述。 Ramda 的 partial
函数接受一个 n
个参数的函数和一个 k
个参数的列表(对于某些 0 < k < n
),return 是一个 [=16] 的新函数=] 将使用您的新参数和原始参数调用原始函数的参数:
const f = (a, b, c, d, e) => a + b + c + d + e;
// f :: a -> b -> c -> d -> e -> a + b + c + d + e
const g = partial(f, [1, 2]);
g.length; //=> 3
g(3, 4, 5); //=> 15
g(3); //=> NaN ≍ 1 + 2 + 3 + undefined + undefined)
// g :: (c, d, e) -> 1 + 2 + c + d + e
函数returned只是剩余参数的简单函数。如果你调用它的次数太少,它就会像你调用原始函数的次数也太少一样。
柯里化是一个稍微不同的故事。在 many languages 中,curry
函数会将 n
参数的函数转换为嵌套的 1 参数函数序列,因此 (a, b, c) => f(a, b, c)
转换为 a => (b => (c => f(a, b, c))
,可以毫无混淆地写成 a => b => c => f(a, b, c)
。在 Ramda 中,我们更灵活一些,允许您在一次调用中提供尽可能多的参数,每次 returning 一个函数,直到您提供了足够的总参数来满足原始函数,在这一点上我们称它为 return 那个值。用例子解释可能更容易:
const f = (a, b, c, d, e) => a + b + c + d + e;
// f :: a -> b -> c -> d -> e -> a + b + c + d + e
const h5 = curry(f);
h5.length; //=> 5
const h3 = h5(1, 2);
h3.length; //=> 3
h3(3, 4, 5); //=> 15
const h2a = h3(3);
h2a.length; //=> 2
h2a(4, 5); //=> 15
const h2b = h5(1, 2, 3);
h2b.length; //=> 2
h2b(4, 5); //=> 15
const h2c = h5(1)(2, 3);
h2c.length; //=> 2
h2c(4, 5); //=> 15
const h2d = h5(1)(2)(3);
h2d.length; //=> 2
h2d(4, 5); //=> 15
const h1a = h3(3, 4);
h1a.length; //=> 1
h1a(5); //=> 15
const h1b = h2a(4);
h1b.length; //=> 1
h1b(5); //=> 15
// h5 :: (a, b, c, d, e) -> a + b + c + d + e
// :: (a, b, c, d) -> e -> a + b + c + d + e
// :: (a, b, c) -> (d, e) -> a + b + c + d + e
// :: (a, b, c) -> d -> e -> a + b + c + d + e
// :: (a, b) -> (c, d, e) -> a + b + c + d + e
// :: (a, b) -> (c, d) -> e -> a + b + c + d + e
// :: (a, b) -> c -> (d, e) -> a + b + c + d + e
// :: (a, b) -> c -> d -> e -> a + b + c + d + e
// :: a -> (b, c, d, e) -> a + b + c + d + e
// :: a -> (b, c, d) -> e -> a + b + c + d + e
// :: a -> (b, c) -> (d, e) -> a + b + c + d + e
// :: a -> (b, c) -> d -> e -> a + b + c + d + e
// :: a -> b -> (c, d, e) -> a + b + c + d + e
// :: a -> b -> (c, d) -> e -> a + b + c + d + e
// :: a -> b -> c -> (d, e) -> a + b + c + d + e
// :: a -> b -> c -> d -> e -> a + b + c + d + e
因为 curry
更加灵活,我自己很少使用 partial
。但是有些人,咳咳,偏向。
curry and partial 函数似乎做同样的事情。 (也许唯一的区别是参数的数量)
这只是方便的问题,还是有充分的理由让两个函数做类似的事情。
正如您简要提到的,curry
函数可以接受一个带有 n 个参数的函数,return n 个带有一个参数的函数。柯里化是将函数组合成高阶函数的重要工具。
函数的部分应用是柯里化的一种。事实上,如果你查看 Ramda source code,你会发现 partial
函数是使用 curry 函数实现的。
var _arity = require('./_arity');
var _curry2 = require('./_curry2');
module.exports = function _createPartialApplicator(concat) {
return _curry2(function(fn, args) {
return _arity(Math.max(0, fn.length - args.length), function() {
return fn.apply(this, concat(args, arguments));
});
});
};
此外,请查看 this question 从根本上解释差异。
来自更广泛的 FP 社区的许多答案可能会误导您。 Ramda 的柯里化在我看来似乎将 ML 风格语言的柯里化精神带入 Javascript,但并不完全相同。
部分应用程序在 Ramda 中可能是相当标准的。 (免责声明:我是 Ramda 的作者之一。)它也更容易描述。 Ramda 的 partial
函数接受一个 n
个参数的函数和一个 k
个参数的列表(对于某些 0 < k < n
),return 是一个 [=16] 的新函数=] 将使用您的新参数和原始参数调用原始函数的参数:
const f = (a, b, c, d, e) => a + b + c + d + e;
// f :: a -> b -> c -> d -> e -> a + b + c + d + e
const g = partial(f, [1, 2]);
g.length; //=> 3
g(3, 4, 5); //=> 15
g(3); //=> NaN ≍ 1 + 2 + 3 + undefined + undefined)
// g :: (c, d, e) -> 1 + 2 + c + d + e
函数returned只是剩余参数的简单函数。如果你调用它的次数太少,它就会像你调用原始函数的次数也太少一样。
柯里化是一个稍微不同的故事。在 many languages 中,curry
函数会将 n
参数的函数转换为嵌套的 1 参数函数序列,因此 (a, b, c) => f(a, b, c)
转换为 a => (b => (c => f(a, b, c))
,可以毫无混淆地写成 a => b => c => f(a, b, c)
。在 Ramda 中,我们更灵活一些,允许您在一次调用中提供尽可能多的参数,每次 returning 一个函数,直到您提供了足够的总参数来满足原始函数,在这一点上我们称它为 return 那个值。用例子解释可能更容易:
const f = (a, b, c, d, e) => a + b + c + d + e;
// f :: a -> b -> c -> d -> e -> a + b + c + d + e
const h5 = curry(f);
h5.length; //=> 5
const h3 = h5(1, 2);
h3.length; //=> 3
h3(3, 4, 5); //=> 15
const h2a = h3(3);
h2a.length; //=> 2
h2a(4, 5); //=> 15
const h2b = h5(1, 2, 3);
h2b.length; //=> 2
h2b(4, 5); //=> 15
const h2c = h5(1)(2, 3);
h2c.length; //=> 2
h2c(4, 5); //=> 15
const h2d = h5(1)(2)(3);
h2d.length; //=> 2
h2d(4, 5); //=> 15
const h1a = h3(3, 4);
h1a.length; //=> 1
h1a(5); //=> 15
const h1b = h2a(4);
h1b.length; //=> 1
h1b(5); //=> 15
// h5 :: (a, b, c, d, e) -> a + b + c + d + e
// :: (a, b, c, d) -> e -> a + b + c + d + e
// :: (a, b, c) -> (d, e) -> a + b + c + d + e
// :: (a, b, c) -> d -> e -> a + b + c + d + e
// :: (a, b) -> (c, d, e) -> a + b + c + d + e
// :: (a, b) -> (c, d) -> e -> a + b + c + d + e
// :: (a, b) -> c -> (d, e) -> a + b + c + d + e
// :: (a, b) -> c -> d -> e -> a + b + c + d + e
// :: a -> (b, c, d, e) -> a + b + c + d + e
// :: a -> (b, c, d) -> e -> a + b + c + d + e
// :: a -> (b, c) -> (d, e) -> a + b + c + d + e
// :: a -> (b, c) -> d -> e -> a + b + c + d + e
// :: a -> b -> (c, d, e) -> a + b + c + d + e
// :: a -> b -> (c, d) -> e -> a + b + c + d + e
// :: a -> b -> c -> (d, e) -> a + b + c + d + e
// :: a -> b -> c -> d -> e -> a + b + c + d + e
因为 curry
更加灵活,我自己很少使用 partial
。但是有些人,咳咳,偏向。