當(dāng)然,這里說的咖哩非彼咖哩,而是currying,譯名柯里化,下面有一個(gè)簡介。
在計(jì)算機(jī)科學(xué)中,柯里化(Currying)是把接受多個(gè)參數(shù)的函數(shù)變換成接受一個(gè)單一參數(shù)(最初函數(shù)的第一個(gè)參數(shù))的函數(shù),并且返回接受余下的參數(shù)且返回結(jié)果的新函數(shù)的技術(shù)。這個(gè)技術(shù)由 Christopher Strachey 以邏輯學(xué)家 Haskell Curry 命名的,盡管它是 Moses Schnfinkel 和 Gottlob Frege 發(fā)明的。
在直覺上,柯里化聲稱“如果你固定某些參數(shù),你將得到接受余下參數(shù)的一個(gè)函數(shù)”。所以對于有兩個(gè)變量的函數(shù)yx,如果固定了 y = 2,則得到有一個(gè)變量的函數(shù) 2x。
在理論計(jì)算機(jī)科學(xué)中,柯里化提供了在簡單的理論模型中比如只接受一個(gè)單一參數(shù)的lambda 演算中研究帶有多個(gè)參數(shù)的函數(shù)的方式
在Swift 3.0以前有一個(gè)咖哩語法,寫法如下
func curry(_ a: Int)(_ b: Int) -> Int {
return a + b
}
let add100 = curry(100)
print(add100(10))
print(curry(100)(10))
curry 并不是一個(gè)接受兩個(gè)參數(shù)的函數(shù),它是一個(gè)接受一個(gè)參數(shù)并返回一個(gè)接受一個(gè)參數(shù)并返回結(jié)果的函數(shù)。說得有點(diǎn)繞口了。這種語法在3.0后被移除了。畢竟也比較少用。
雖然語法在3.0里被移除了,但我們可以人為的實(shí)現(xiàn)一個(gè),而且更好更強(qiáng)大的咖哩。實(shí)現(xiàn)如下
func curry<T, U, O>(_ f:@escaping (T, U) -> O) -> (T) -> (U) -> O {
return { a in return { b in return f(a, b) } }
}
func add(a: Int, b: Int) -> Int {
return a + b
}
let add100 = curry(add)(100)
print(add100(10))
curry 函數(shù)接收另一個(gè)函數(shù)并將其煮成一盤咖哩。
寫到這里我想到了一個(gè)段子
有一天老板要求你寫一個(gè)整型加10的函數(shù),然后你寫了一個(gè)
func add10(a: Int) -> Int { return a + 10 }
第二天老板要你改成加10吧,你只好改為
func add20(a: Int) -> Int { return a + 20 }
第三天老板又來找你了,這次他不確定要加多少,你就寫一個(gè)能加任何數(shù)字的函數(shù)吧
當(dāng)然聰明的你大概從一開始就考慮寫一個(gè)接受兩個(gè)整型的函數(shù)吧,這樣就不會有這個(gè)段子了。但如果老板說每個(gè)函數(shù)只能接受一個(gè)參數(shù)呢?
實(shí)際上curry這個(gè)函數(shù)不只是單純的實(shí)現(xiàn)兩個(gè)整形的加法,它還有更多的用途,例如下面
func curry<T, U, O>(_ f:@escaping (T, U) -> O) -> (T) -> (U) -> O {
return { a in return { b in return f(a, b) } }
}
let users = [["id":1, "name":"Noah"], ["id":2, "name":"Kell"]]
let get = curry{ (key:String, dict:[String:Any]) in return
dict[key]
}
let names = users.flatMap(get("name"))
curry函數(shù)是泛型的,它可以將任何接收兩個(gè)參數(shù)的函數(shù)煮成咖哩。上面一個(gè)比較簡單的例子可以看得出來。
看到這里自然會問它有什么用,用在什么場景下了。實(shí)際上它的裝飾用途大于實(shí)際用途,例如你在使用函數(shù)式思想進(jìn)行編程的時(shí)候更多的是使用陳述語句“ declarative”而不是常規(guī)編程的指令式“imperative”語句。
下面用上面的例子再提供兩個(gè)腳本語言的寫法,Python和Javascript es6的。從簡潔上來看JS es6是完勝的,而且更好理解。
let curry = f => a => b => f(a, b);
let users = [{id: 1, name: 'Noah'}, {id: 2, name: 'Kell'}];
get = curry((property, object) => object[property]);
names = users.map(get('name'));
def curry(f):
return lambda a: lambda b: f(a, b)
users = [{'id': 1, 'name': 'Noah'}, {'id': 2, 'name': 'Kell'}]
get = curry(lambda k, d: d[k])
names = map(get("name"), users)