JS中的 偏函数 和 柯里化

什么是偏函数

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

举个例子:java

// 咱们建立了一个作 乘法运算 的函数
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为第一个参数。其余参数等待传入。api

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

何时使用偏函数

刚刚咱们基于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),代表该请求发送自同一用户签名。post

什么是柯里化

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

函数式编程语言 和 其余许多语言 中,柯里化(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.  在不少状况下,咱们能够更方便的生成 偏函数 变体,能够更灵活的被咱们使用。

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

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

相关文章
相关标签/搜索