前兩天看Wikipedia上的Currying時,突然頓悟了(可見之前都沒認真)。這個不看英文Wikipedia的說明,還真的是不容易看懂。簡單的說,假設有個 function 是 f( x, y, z ),currying 就是令 f1=f(1),當呼叫 f1( 2, 3 ) 時,就等於是呼叫 f( 1, 2, 3 )。
目前想到可以應用在 c/c# 沒辦法帶預設參數的情況上,像:[c]void func( int x, int y, int z ) { }
void new_func1( int y, int z ) { return func( 1, y, z ); }
void new_func2( int y, int z ) { return func( 2, y, z ); }
[/c]
你可以想像到,這是一項複製、貼上的體力活,用 currying 的話,可以很快創造出新函數:[c]/* 以下為虛擬碼 */
void func( int x, int y, int z ) { }
new_func1 = func(1); /* new_func1 仍是函數 */
new_func2 = func(2); /* new_func2 仍是函數 */
[/c]
在 python 裡,透過 *arg、**kwargs 可以很容易實現,文章可以參考 Currying and Python, a practical example,裏面有點複雜,其實只要看 curry 類別的部份,下面就是直接摘錄出來的實例:[python]class curry:
def __init__(self, fun, *args, **kwargs):
self.fun = fun
self.pending = args[:]
self.kwargs = kwargs.copy()
def __call__(self, *args, **kwargs):
if kwargs and self.kwargs:
kw = self.kwargs.copy()
kw.update(kwargs)
else:
kw = kwargs or self.kwargs
return self.fun(*(self.pending + args), **kw)
def func( a, b, c ):
print( a, b, c )
return (a+b+c)
func1 = curry( func, 1, 100 )
func2 = curry( func, 2 )
print( func1( 200 ), func2( 300, 400 ) )
[/python]
原理就是利用類別的特殊方法 __call__ ,呼叫 func2=curry( func, 2 ) 時,實際上是得到 curry 類別的實體。curry 類別的 __init__ 裡去做參數的判斷跟預存,等到把 func2 當函數執行時,就會執行到 __call__,這裡再去呼叫真正的函數。