什麼是函式柯里化(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
複製程式碼
partial
函式可以幫我們生成一個偏函式。- 呼叫
partial
函式生成一個偏函式並賦值給parMulti
,其中預設一個值1
。 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
。偏函式通過設定預設值,幫我們實現程式碼上的複用。
總結
柯里化和偏函式都是用於將多個引數函式,轉化為接受更少引數函式的方法。傳入部分引數後,處於中間狀態的函式可以作為固定值進行復用。但是其中不同之處在於:
- 柯里化是將函式轉化為多個巢狀的一元函式,也就是每個函式只接受一個引數。
- 偏函式可以接受不只一個引數,它被固定了部分引數作為預設,並可以接受剩餘的引數。