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.  在不少狀況下,咱們能夠更方便的生成 偏函數 變體,能夠更靈活的被咱們使用。

總得來講,柯里化的目的 就是在不影響 初始函數 的調用形式的狀況下,更方便的讓咱們得到初始函數的 偏函數 變體。

最後,歡迎各位小夥伴留言,相互討論。

相關文章
相關標籤/搜索