See the Elephant

1992生まれのプログラマが書くエンジニアブログ

curry化と部分適用の違いについて

今日は, rubycurryを勉強していた.

ruby 2.4.0の日本語リファレンスMethod#curryによるとこう書かれている.

self を元にカリー化した Proc を返します。カリー化した Proc はいくつかの引数をとります。十分な数の引数が与えられると、元の Proc に引数を渡して実行し、結果を返します。引数 の個数が足りないときは、部分適用したカリー化 Proc を返します

か, カリー化ってなんだ….?

wikiによるとカリー化とはこういう意味らしい.

カリー化 (currying, カリー化された=curried) とは、複数の引数をとる関数を、引数が「もとの関数の最初の引数」で戻り値が「もとの関数の残りの引数を取り結果を返す関数」であるような関数にすること(あるいはその関数のこと)である。

ふーん. なるほど.

ということは,

div  = lambda {|x, y| x.to_f/y.to_f}.curry # ラムダ式をカリー化
div1 = add.(1) # xを1に束縛, 
div1.(4) # yを4に束縛, このタイミングでprocが実行される

#=> 5

これで俺もカレー職人や!!!

と思っていたらこれはどうやら部分適用と呼ぶらしい.

カリー化と部分適用の違い

curry化と部分適用を混同してる人が多いと書かれたブログを見つけた. 部分適用をカリー化と呼ばないで - kmizuの日記

いまいちピンと来なかったので, 調べてみると こんな書き込みを見つけた(Shingo Omura @everpeace)

x,y,z -> V をx -> (y->(z->V)) に変換するのがカリー化。x,y,z-> Vのyに値を束縛して結果的にx,z->Vという関数になるのが部分適用。どこが同じなんだろうか。

なるほど. 階層化することが大切なんだね. でもいまいちピンと来ていない.

見落としていたけど, wikiの下の方にこんなことが書いてあった.


カリー化は部分適用と混同されやすい。

部分適用とは、

複数引数ある関数の引数の一部だけに実引数を適用する操作

のことで、div->invを導出する操作を指す。

一方、カリー化は

div->cdivを導出する操作であり、引数への値の適用までは行わない.

div = lambda {|x, y| x.to_f/y.to_f}
cdiv = lambda {|x| lambda{|y| div.(x,y)}}
inv = cdiv.(1)
p inv.(2) #=> 0.5

つまり, 値を入れた時点で「部分適用」になるんですね.

wikiによると, カリー化は以下のように表現されていた.

...Haskellでは関数は常に一つの引数のみを取り、複数の引数を取る関数とは、単にネストされた複数の一引数関数の糖衣構文(syntax suger)にすぎない

なるほど.

今読み返すと@everpeaceさんの表現がとてもわかり易い.

x,y,z -> V をx -> (y->(z->V)) に変換するのがカリー化。x,y,z-> Vのyに値を束縛して結果的にx,z->Vという関数になるのが部分適用。どこが同じなんだろうか。

調べてみて, 僕の中での「カリー化」の理解は, 以下のような感じでまとまった.

カリー化 = 複数の引数を取る関数を, 常に1つの引数を取る関数が多層化された関数に変換すること