當然,這里說的咖哩非彼咖哩,而是currying,譯名柯里化,下面有一個簡介。
在計算機科學中,柯里化(Currying)是把接受多個參數的函數變換成接受一個單一參數(最初函數的第一個參數)的函數,并且返回接受余下的參數且返回結果的新函數的技術。這個技術由 Christopher Strachey 以邏輯學家 Haskell Curry 命名的,盡管它是 Moses Schnfinkel 和 Gottlob Frege 發明的。
在直覺上,柯里化聲稱“如果你固定某些參數,你將得到接受余下參數的一個函數”。所以對于有兩個變量的函數yx,如果固定了 y = 2,則得到有一個變量的函數 2x。
在理論計算機科學中,柯里化提供了在簡單的理論模型中比如只接受一個單一參數的lambda 演算中研究帶有多個參數的函數的方式
在Swift 3.0以前有一個咖哩語法,寫法如下
func curry(_ a: Int)(_ b: Int) -> Int {
return a + b
}
let add100 = curry(100)
print(add100(10))
print(curry(100)(10))
curry 并不是一個接受兩個參數的函數,它是一個接受一個參數并返回一個接受一個參數并返回結果的函數。說得有點繞口了。這種語法在3.0后被移除了。畢竟也比較少用。
雖然語法在3.0里被移除了,但我們可以人為的實現一個,而且更好更強大的咖哩。實現如下
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 函數接收另一個函數并將其煮成一盤咖哩。
寫到這里我想到了一個段子
有一天老板要求你寫一個整型加10的函數,然后你寫了一個
func add10(a: Int) -> Int { return a + 10 }
第二天老板要你改成加10吧,你只好改為
func add20(a: Int) -> Int { return a + 20 }
第三天老板又來找你了,這次他不確定要加多少,你就寫一個能加任何數字的函數吧
當然聰明的你大概從一開始就考慮寫一個接受兩個整型的函數吧,這樣就不會有這個段子了。但如果老板說每個函數只能接受一個參數呢?
實際上curry這個函數不只是單純的實現兩個整形的加法,它還有更多的用途,例如下面
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函數是泛型的,它可以將任何接收兩個參數的函數煮成咖哩。上面一個比較簡單的例子可以看得出來。
看到這里自然會問它有什么用,用在什么場景下了。實際上它的裝飾用途大于實際用途,例如你在使用函數式思想進行編程的時候更多的是使用陳述語句“ declarative”而不是常規編程的指令式“imperative”語句。
下面用上面的例子再提供兩個腳本語言的寫法,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)