JavaScript 柯里化
最后修改于 2023 年 10 月 18 日
JavaScript 柯里化教程定义了柯里化转换,并在实际示例中进行了演示。
柯里化
柯里化是将一个具有多个参数的函数转换成一系列嵌套的只有一个参数的函数的过程。柯里化允许进行函数特化和组合。
我们可以将 fn(a,b,c) 转换为 fn(a)(b)(c)。
柯里化函数是高阶函数,允许我们创建原始函数的特化版本。柯里化之所以有效,是因为闭包,闭包在返回后保留了封闭的函数作用域。
Lodash 包含了 _.curry 函数,它可以将一个普通函数转换为柯里化函数。在 Ramda 中,所有函数都是自动柯里化的。
JS 柯里化基本示例
以下基本示例使用柯里化。
function multiply(a, b, c) {
return a * b * c;
}
function multiply_curried(a) {
return function (b) {
return function (c) {
return a * b * c
}
}
}
let res = multiply(1, 2, 3);
console.log(res);
let mc1 = multiply_curried(1);
let mc2 = mc1(2);
let res2 = mc2(3);
console.log(res2);
let res3 = multiply_curried(1)(2)(3);
console.log(res3);
我们有一个普通的 multiply 函数,它将它的三个参数相乘。代码 multiply_curried 使用柯里化来完成乘法运算。
let res = multiply(1, 2, 3);
这是一个经典的函数调用;它的所有参数都放在圆括号之间传递。
let mc1 = multiply_curried(1); let mc2 = mc1(2); let res2 = mc2(3); console.log(res2);
在柯里化中,函数接受一个参数并返回另一个函数,该函数接受下一个参数,直到所有参数都被使用完。
let res3 = multiply_curried(1)(2)(3);
这是前一个代码的简写形式。
$ node basic.js 6 6 6
JS 柯里化基本示例 II
下一个示例使用箭头函数来创建普通函数和柯里化函数。
let multiply = (a, b, c) => {
return a * b * c;
}
let multiply_curried = (a) => (b) => (c) => {
return a * b * c;
}
let res = multiply(1, 2, 3);
console.log(res);
let res2 = multiply_curried(1)(2)(3);
console.log(res2);
乘法函数使用箭头函数语法重写。
JS 反柯里化
可以将柯里化函数进行反柯里化。
let multiply_curried = (a) => (b) => (c) => {
return a * b * c;
}
let multiply = (a, b, c) => multiply_curried(a)(b)(c);
let res = multiply(2,3,4);
console.log(res);
let res2 = multiply_curried(2)(3)(4);
console.log(res2);
该示例将 multiply_curried 转换为 multiply 函数。
JS 柯里化 - 通用函数
我们创建一个通用函数,该函数检查单词是否包含 n 个字符。
let hasNChars = (n=3) => (word) => word.length === n;
let words = ['forest', 'gum', 'pencil', 'wonderful', 'grace',
'table', 'lamp', 'biblical', 'midnight', 'or', 'perseverance',
'adminition', 'redemption', 'dog', 'no'];
let res = words.some(hasNChars(2), words);
console.log(res);
let res2 = words.some(hasNChars, words);
console.log(res2);
有一个单词数组。hasNChars 函数首先获取 n 值,然后获取要检查的单词。如果我们不提供 n 值,则使用默认值 3。
let res = words.some(hasNChars(2), words);
使用数组的 some 函数,我们检查是否有一个单词包含两个字符。
let res2 = words.some(hasNChars, words);
这里我们检查是否有一个单词包含两个字母。
$ node hasnchars.js true true
JS 柯里化 - 特化函数
特化函数源自更通用的函数。
let greet = (message) => (name) => {
return `${message} ${name}!`;
}
let helloGreet = greet('Hello');
console.log(greet('Good day')('Lucia'));
console.log(helloGreet('Peter'));
有一个柯里化的 greet 函数。特化函数 helloGreet 源自 greet 函数。
$ node specialized.js Good day Lucia! Hello Peter!
JS 柯里化 - 函数组合
函数组合允许组合任意数量的函数来创建一个新函数。
let double = x => x * 2
let triple = x => x * 3
let quadruple = x => x * 4
let pipe = (...funs) => input => funs.reduce(
(total, fn) => fn(total),
input
)
let fun1 = pipe(double)
let fun2 = pipe(double, triple)
let fun3 = pipe(triple, triple)
let fun4 = pipe(double, triple, quadruple)
console.log(fun1(2))
console.log(fun2(5))
console.log(fun3(7))
console.log(fun4(9))
pipe 函数接受任意数量的参数 - 函数。组合函数稍后接受函数操作的输入值。为了创建 pipe 函数,我们使用 reduce 函数。阅读 JavaScript reduce 教程 了解更多关于 reduce 函数的信息。
$ node composing.js 4 30 63 216
Lodash _.curry
Lodash 是一个强大的库,它为常见的编程任务提供了实用函数。_.curry 函数将一个普通函数转换为柯里化函数。
$ npm i lodash
我们需要安装 Lodash 库。
const _ = require("lodash");
function multiply(a, b, c) {
return a * b * c;
}
let curried = _.curry(multiply);
let ret = curried(2)(3)(4);
console.log(ret);
在这个例子中,我们将 multiply 函数转换为柯里化版本。
Ramda 自动柯里化
Ramda 是一个实用的 JavaScript 函数式编程库。该库侧重于不变性和无副作用的函数。Ramda 函数也自动进行柯里化。
$ npm i ramda
我们需要安装 Ramda 库。
const R = require('ramda');
let fn1 = R.add(5);
let res = fn1(6);
console.log(res);
let fn2 = R.divide(100);
let res2 = fn2(20);
console.log(res2);
我们在 R.add 和 R.divide 函数中演示了自动柯里化。
$ node auto_curry.js 11 5
JS 自定义柯里化方法
最后,我们创建自定义的柯里化方法。
function multiply(a, b, c) {
return a * b * c;
}
function curry(func) {
return function curried(...args) {
console.log(args);
if (args.length >= func.length) {
return func.apply(this, args);
} else {
console.log('calling else');
return function(...args2) {
return curried.apply(this, args.concat(args2));
}
}
};
}
let curried = curry(multiply);
console.log(curried(1, 2, 3));
console.log(curried(1)(2, 3));
console.log(curried(1)(2)(3));
自定义的 curry 方法是使用递归创建的。我们还使用了展开运算符、apply 函数和函数 length 属性。
length 属性返回函数接收的参数数量。apply 函数使用给定的 this 值和作为数组提供的参数来调用给定的函数。
let curried = curry(multiply);
我们使用自定义的 curry 函数创建了乘法函数的柯里化版本。
console.log(curried(1, 2, 3));
curried 函数仍然可以正常调用。
console.log(curried(1)(2, 3));
在这一行中,我们正在柯里化第一个参数。
console.log(curried(1)(2)(3));
我们柯里化所有三个参数。
$ node mycurry.js [ 1, 2, 3 ] 6 [ 1 ] [ 1, 2, 3 ] 6 [ 1 ] [ 1, 2 ] [ 1, 2, 3 ] 6
来源
在本文中,我们研究了 JavaScript 中的柯里化转换过程。我们还简要提到了 Lodash 和 Ramda 函数式库。