JS中的 偏函数 和 柯里化

什么是偏函数

在计算机科学中,偏函数应用 是指将一些参数固定到一个函数,产生另一个较小的函数的过程。

举个例子:

// 我们创建了一个做 乘法运算 的函数
function mult(a, b) {
    return a * b;
};复制代码

基于这个函数,我们利用bind创造一个新的函数 double

let double = mult.bind(null, 2);

console.log( double(3) );  // mult(2, 3) = 6;
console.log( double(4) );  // mult(2, 4) = 8;
console.log( double(5) );  // mult(2, 5) = 10; 复制代码

mult.bind(null, 2) 创造了一个新函数 double,传递调用到mult函数,以nullcontext2为第一个参数。其他参数等待传入。

这就是 偏函数应用 —— 我们创造了一个新函数,同时将部分参数替换成特定值。

什么时候使用偏函数

刚刚我们基于mult创建了一个新的函数double,这里我们再创建一个新的函数triple

let triple = mult.bind(null, 3);

console.log( triple(3) );  // mult(3, 3) = 9;
console.log( triple(4) );  // mult(3, 4) = 12;
console.log( triple(5) );  // mult(3, 5) = 15;复制代码

使用 偏函数,我们能够从中受益的是:我们创建了一个独立的非匿名函数(doubletriple)。我们可以使用它,而不需要每次都传入第一个参数,因为bind帮我们搞定了。

在其他的场景中,当我们有一个非常通用的函数,并且想要方便地获取它的特定变体,偏函数也是非常有用。

举个例子,我们拥有函数post(signature, api, data)。在调用请求方法的时候有着相同的用户签名,这里我们想要使用它的偏函数变体:post(api, data),表明该请求发送自同一用户签名。

什么是柯里化

有时候人们会把 偏函数应用 和另外一个名为 柯里化 的东西混淆,但那的确是另外一个和函数有关的有趣技术。

函数式编程语言 和 其他许多语言 中,柯里化(currying)提供了一种自动管理参数如何传递给函数和异常的方法。

简单来说,currying 是一项将一个调用形式为f(a, b, c)的函数转化为调用形式f(a)(b)(c)的技术。

举个例子:

function currying(fn) {
    return function(a) {
        return function(b) {
            return fn(a, b);
        };
    };
}

// 用法
function sum(a + b) {
    return a + b;
}

let carriedSum = currying(sum);

console.log( carriedSum(1)(2) );  // 3复制代码

从上面的例子可以看到,carrying的实现就是一系列的封装。

  1.  currying(fn) 的结果就是一层封装function(a)
  2. 当它被调用,就像 carriedSum(1) 这样,它的参数被保存到 词法环境 中,然后返回一层新的封装function(b)
  3. 然后carriedSum(1)(2)调用function(b),传入参数2,它将调用传递给初始的多参数函数sum

结合之前的 偏函数 知识,我们可以看到,sum函数在 柯里化 之后,逐个传递参数的时候返回的那一层封装:其实就是 sum函数 的 偏函数变体

这可能也是大家容易将这个两个概念搞混的原因之一吧。

高级柯里化

高级的柯里化同时允许 函数正常调用获取偏函数

我们可以实现一下 高级的柯里化:

function currying(fn) {
    return function curried(...args) {
        if(args.length>=fn.length) {
            // 传入的实参长度 >= 初始函数形参长度 的时候,则直接执行初始函数
            return fn.apply(this, args);
        } else {
            // 否则 得到一个 偏函数,递归carried方法,直到获得所有参数后,直接执行
            return function(...args2) {
                return curried.apply(this, args.concat(args2));
            }
        }
    }
}

function sum(a, b, c) {
    return a + b + c;
}

let curriedSum = currying(sum);

// 常规调用
console.log( curriedSum(1, 2, 3) );  // 6

// 得到 curriedSum(1)的偏函数,然后用另外两个参数调用它
console.log( curriedSum(1)(2, 3) );  // 6

// 完全柯里化调用
console.log( curriedSum(1)(2)(3) );  // 6复制代码

柯里化的目的是什么?

从上面的 高级柯里化 实现的例子中可以发现:

  1.  sum函数 在 柯里化 之后对于使用并没有任何影响,仍然可以被正常调用。
  2.  在很多情况下,我们可以更方便的生成 偏函数 变体,可以更灵活的被我们使用。

总得来说,柯里化的目的 就是在不影响 初始函数 的调用形式的情况下,更方便的让我们获得初始函数的 偏函数 变体。

最后,欢迎各位小伙伴留言,相互讨论。


转载于:https://juejin.im/post/5cd0f958f265da03a22f7a33

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值