JavaScript currying

JavaScript currying 教程定义了 currying 转换并在实际示例中进行了演示。

柯里化

Currying 是将具有多个参数的函数转换为具有单个参数的一系列嵌套函数。柯里化允许执行函数专业化和组合。

我们可以将fn(a,b,c) 可调用对象转换为fn(a)(b)(c)

柯里化函数是高阶函数,它允许我们创建原始函数的专用版本。柯里化的工作归功于闭包,闭包在返回后保留封闭的函数范围。

Lodash 包含_.curry 函数,可以将普通函数转换为柯里化函数。在 Ramda 中,所有函数都是自动柯里化的。

JS curry 基本示例

以下基本示例使用柯里化。

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 curry 基础实例二

下一个示例使用箭头函数来创建普通函数和柯里化函数。

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 curry – 泛型函数

我们创建了一个通用函数来检查单词是否有 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 curry – 特殊函数

专用函数派生自更通用的函数。

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 curry – 函数组合

函数组合允许组合任意数量的函数来创建一个新函数。

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 函数。阅读 JavaScriptreduce 教程以了解有关 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.addR.divide 函数中演示了自动柯里化。

$ node auto_curry.js 
11
5

JS自定义curry方法

最后,我们创建自定义 curry 方法。

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 功能库。

列出所有 JavaScript 教程。

赞(0) 打赏

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏