函式柯里化和偏函式應用

LanceT發表於2019-04-07

什麼是函式柯里化(Currying)


維基百科中的定義:

在電腦科學中,柯里化(英語:Currying),又譯為卡瑞化或加里化,是把接受多個引數的函式變換成接受一個單一引數(最初函式的第一個引數)的函式,並且返回接受餘下的引數而且返回結果的新函式的技術。

換句話說就是把一個有n個引數的函式轉換成n個巢狀的函式,每個函式只接受一個引數,並返回一個新函式。也就是把f(a,b,c)轉化為f(a)(b)(c)

 

例子


例如一個函式接受a, b, c三個引數,返回它們的乘積:

function multiply(a, b, c) {
    return a * b * c;
}
複製程式碼

執行此函式,我們在引數中依次傳入1,2,3,返回6。

multiply(1,2,3); // 6

複製程式碼

讓我們看一下柯里化之後的樣子:

function multiply(a) {
    return (b) => {
        return (c) => {
            return a * b * c
        }
    }
}
multiply(1)(2)(3) // 6
複製程式碼

我們把multiply(1,2,3)變成了multiply(1)(2)(3)的形式。通過把一個多參函式轉換成一系列巢狀的函式,每個函式依次接受一個引數,這就是函式柯里化。

或者換一種寫法可以更容易理解:

function multiply(a) {
    return (b) => {
        return (c) => {
            return a * b * c
        }
    }
}
const mul1 = multiply(1);
const mul2 = mul1(2);
const result = mul2(3);  // 6
複製程式碼

multiply函式接受一個a引數,我們傳入1。mult1等於multiply(1)就等於

(b) => {
    return (c) => {
        return a * b * c
    }
}
複製程式碼

並且接受一個引數b。以此類推,mul1(2)傳入2,然後賦值給mult2等於

(c) => {
    return a * b * c
}
複製程式碼

最後mul2(3)返回a * b * c。由於閉包的特性會儲存之前傳入的值1和2,最後得出6。

 

函式柯里化的應用


  • 柯里化函式避免我們重複傳參,實現複用

假設我們有一個函式用來計算三個物體的體積。

function volume(l, w, h) {
    return l * w * h;
}
volume(200,30,100) // 2003000
volume(32,45,100); //144000
volume(2322,232,100) // 53870400
複製程式碼

從上面程式碼可以看出,如果每個物體的高度都是100,我們需要重複傳三次。 如果使用柯里化函式可以避免:

function volume(h) {
    return (w) => {
        return (l) => {
            return l * w * h
        }
    }
}

//固定高度
const itemHeight = volume(100);

//計算其他不同情況
itemHeight(30)(200); 
itemHeight(45)(32); 
itemHeight(232)(2322); 
複製程式碼
  • 函式的合成

合成兩個函式的簡單程式碼如下:

var compose = function(f,g) {
  return function(x) {
    return f(g(x));
  };
};
複製程式碼

f(x)g(x)合成為f(g(x)),有一個隱藏的前提,就是f和g都只能接受一個引數, 其中就運用了函式柯里化。

 

什麼是偏函式應用(Partial Application)


Partial Application(偏函式應用)很容易和函式柯里化混淆,它是指使用一個函式並將其應用一個或多個引數,但不是全部引數,在這個過程中建立一個新函式,這個函式用於接受剩餘的引數。這段話很不容易理解,具體看下面的例子:

 

例子


function multiply(a,b,c){
    return a * b * c;
}
//生產偏函式的工廠
function partial(fn,a){
    return function(b,c){
        return fn(a,b,c);
    }
}

//變數parMulti接受返回的新函式
var parMulti = partial(multiply,1);

//在呼叫的時候傳入剩餘的引數
parMulti(2,3); // 6
複製程式碼
  1. partial函式可以幫我們生成一個偏函式。
  2. 呼叫partial函式生成一個偏函式並賦值給parMulti,其中預設一個值1
  3. parMulti接受剩餘引數2,3結合前面預設的1得出最終結果。

 

偏函式的應用


例如bind函式可以讓我們傳入一個或多個想要預設的引數,之後返回一個新函式,並擁有指定的this值和預設引數。當繫結函式被呼叫時,這些引數會被插入到目標函式的引數列表的開始位置,傳遞給繫結函式的引數會跟在它們後面。

function addition(x, y) {
   return x + y;
}
const plus5 = addition.bind(null, 5)
plus5(10) // output -> 15
plus5(20) // output -> 25
複製程式碼

我們預先傳入了引數5,並返回了一個新函式賦值給plus5,此函式可以接受剩餘的引數。呼叫plus5傳入剩餘引數10得出最終結果15,如傳入20得出25。偏函式通過設定預設值,幫我們實現程式碼上的複用。

 

總結


柯里化和偏函式都是用於將多個引數函式,轉化為接受更少引數函式的方法。傳入部分引數後,處於中間狀態的函式可以作為固定值進行復用。但是其中不同之處在於:

  • 柯里化是將函式轉化為多個巢狀的一元函式,也就是每個函式只接受一個引數。
  • 偏函式可以接受不只一個引數,它被固定了部分引數作為預設,並可以接受剩餘的引數。

相關文章