【名詞解釋】Currying:因為是美國數(shù)理邏輯學家哈斯凱爾·加里(Haskell Curry)發(fā)明了這種函數(shù)使用技巧,所以這樣用法就以他的名字命名為 Currying沉唠,中文翻譯為“柯里化”仲锄。
我感覺很多人都對函數(shù)柯里化(Currying)和偏函數(shù)應用(Partial Application)之間的區(qū)別搞不清楚署鸡,尤其是在相似的上下文環(huán)境中它們同時出現(xiàn)的時候额嘿。
偏函數(shù)解決這樣的問題:如果我們有函數(shù)是多個參數(shù)的交洗,我們希望能固定其中某幾個參數(shù)的值愧哟。
幾乎所有編程語言中都有非常明顯的偏函數(shù)應用奥吩。在C語言中:
int foo (int a, int b, int c) {
return a + b + c;
}
int foo23(int a, int c) {
return foo (a, 23, c);
}
foo23 函數(shù)實際上就是一個 foo 函數(shù)的偏函數(shù)應用,參數(shù) b 的值被固定為 23蕊梧。
當然霞赫,像這樣明顯的偏函數(shù)并沒有太大的用處;我們通常會希望編程語言能提供我們某些偏函數(shù)特征肥矢。
例如端衰,在 Python 語言中,我們可以這樣做:
from functools import partial
def foo (a,b,c):
return a + b + c
foo23 = partial (foo, b=23)
foo23(a = 1, c = 3) # => 27
函數(shù)柯里化(Currying)明顯解決的是一個完全不同的問題:如果我們有幾個 單參數(shù) 函數(shù)橄抹,并且這是一種支持一等函數(shù)(first-class)的語言靴迫,如何去實現(xiàn)一個多參數(shù)函數(shù)?函數(shù)柯里化是一種 實現(xiàn)多參數(shù)函數(shù)的方法楼誓。
下面是一個單參數(shù)的 Javascript 函數(shù):
var foo = function(a) {
return a * a;
}
如果我們受限只能寫單參數(shù)函數(shù)玉锌,可以像下面這樣模擬出一個多參數(shù)函數(shù):
var foo = function(a) {
return function(b) {
return a * a + b * b;
}
}
通過這樣調(diào)用它: (foo (3))(4) ,或直接 foo (3)(4) 疟羹。
注意主守,函數(shù)柯里化提供了一種非常自然的方式來實現(xiàn)某些偏函數(shù)應用。如果你希望函數(shù) foo 的第一個參數(shù)值被固定成5榄融,你需要做的就是 var foo5 = foo (5) 参淫。這就 OK 了。函數(shù) foo5 就是 foo 函數(shù)的偏函數(shù)愧杯。注意涎才,盡管如此,我們沒有很簡單的方法對 foo 函數(shù)的第二個參數(shù)偏函數(shù)化(除非先偏函數(shù)化第一個參數(shù))力九。
當然耍铜,Javascript 是支持多參數(shù)函數(shù)的:
var bar = function(a, b) {
return a * a + b * b;
}
我們定義的 bar 函數(shù)并不是一個柯里化的函數(shù)。調(diào)用 bar (5) 并不會返回一個可以輸入 12 的函數(shù)跌前。我們只能像 bar (5,12) 這樣調(diào)用這個函數(shù)棕兼。
在一些其它語言里,比如 Haskell 和 OCaml抵乓,所有的多參數(shù)函數(shù)都是通過柯里化實現(xiàn)的伴挚。
下面是一個把上面的 foo 函數(shù)用 OCaml 語言寫成的例子:
let foo = fun a ->
fun b ->
a * a + b * b
下面是把上面的 bar 函數(shù)用 OCaml 語言寫成的例子:
let bar = fun a b ->
a * a + b * b
頭一個函數(shù)我們叫做“顯式柯里化”靶衍,第二個叫做“隱式柯里化”。
跟 Javascript 不一樣茎芋,在 OCaml 語言里颅眶, foo 函數(shù)和 bar 函數(shù)是完全一樣的。我們用完全一樣的方式調(diào)用它們败徊。
# foo 3 4;;
- : int = 25
# bar 3 4;;
- : int = 25
兩個函數(shù)都能夠通過提供一個參數(shù)值來創(chuàng)造一個偏函數(shù):
# let foo5 = foo 5;;
val foo5 : int -> int = <fun>
# let bar5 = bar 5;;
val bar5 : int -> int = <fun>
# foo5 12;;
- : int = 169
# bar5 12;;
- : int = 169
事實上帚呼,我們可以把下面這個匿名函數(shù):
fun arg1 arg2 ... argN -> exp
當作是下面這個函數(shù)的簡寫:
fun arg1 -> fun arg2 -> ... -> fun argN -> exp
函數(shù)柯里化和偏函數(shù)應用的總結(jié)
偏函數(shù)應用是找一個函數(shù),固定其中的幾個參數(shù)值皱蹦,從而得到一個新的函數(shù)煤杀。
函數(shù)柯里化是一種使用匿名單參數(shù)函數(shù)來實現(xiàn)多參數(shù)函數(shù)的方法。
函數(shù)柯里化能夠讓你輕松的實現(xiàn)某些偏函數(shù)應用沪哺。
有些語言(例如 Haskell, OCaml)所有的多參函數(shù)都是在內(nèi)部通過函數(shù)柯里化實現(xiàn)的沈自。