ラムダ式クロージャのカリー化
hottyです。
今回はクロージャのカリー化のお話です。
クロージャとはなにか?
クロージャについては今更説明することもあまりありませんが一応。
クロージャとは静的なスコープを持った関数であり Rubyだと手続きオブジェクトと呼ばれるものです。
ブロックをオブジェクト化したものとイメージすると良いかもしれません。
実際ブロックとクロージャは密接な関係にあります。
クロージャをブロックとして評価したり ブロック引数でブロックをクロージャとして受け取る事が可能です。
Rubyでクロージャを定義するにはlambda、proc、Proc.newなどの方法があります。
lambdaで作るクロージャはジャンプ構文や引数の取り扱いがproc、Proc.newとは異なるので注意が必要です。
さてクロージャの話はそこそこに今回の本題「カリー化について」です。
カリー化とはなにか?
Wikiでカリー化は
複数の引数をとる関数を、引数が「もとの関数の最初の引数」で戻り値が「もとの関数の残りの引数を取り結果を返す関数」であるような関数にすること。
と定義されています。
んー、文章だけではちょっと何言ってるかわからないです…。
実際に例を見たほうがわかりやすいかもしれません。
例えば以下のような関数があります。
1 2 3 4 5 6 7 8 9 10 11 |
[sourcecode lang="ruby"] def compare(a, b) if a > b return a elsif b > a return b else return nil end end [/sourcecode] |
見ての通り aとbを比べて大きい方(aとbが同じだったらnil)を返す関数です。
例えばこの関数を使ってある5つの数(0,5,10,99,9999)が10より大きいか比較したいと思います。
1 2 3 4 5 6 7 8 |
[sourcecode lang="ruby"] i = 10 puts compare(i, 0) puts compare(i, 5) puts compare(i, 10) puts compare(i, 99) puts compare(i, 9999) [/sourcecode] |
と このように書く事ができますね。
もしこのcompareという関数がカリー化されていたらどうなるか。
次の例で実際にcompareをカリー化して書きなおしてみます。
カリー化してみる
1 2 3 4 5 6 7 8 9 10 11 12 13 |
[sourcecode lang="ruby"] def compare(a) return lambda do |b| if a > b return a elsif b > a return b else return nil end end end [/sourcecode] |
これがカリー化です。
複数の引数(a, b)をとる関数compareを、引数が「もとの関数の最初の引数(a)」で戻り値が「もとの関数の残りの引数(b)を取り 結果を返す関数」であるような関数になっていますね!
カリー化させたcompareで5つの数(0,5,10,99,9999)が10より大きいか比較するにはこのように書きます。
1 2 3 4 5 6 7 8 9 |
[sourcecode lang="ruby"] func = compare(10) puts func.call(0) puts func.call(5) puts func.call(10) puts func.call(99) puts func.call(9999) [/sourcecode] |
カリー化する事で簡潔に見やすく書くことができます。
クロージャをカリー化してみる
さてここでクロージャが登場します。
クロージャをカリー化する方法ではもっと簡単な方法があります。
ずばり Procクラスのcurryメソッドを使う方法です。
同じように例を書いてみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
[sourcecode lang="ruby"] compare = lambda do |a, b| if a > b return a elsif b > a return b else return nil end end curry = compare.curry # ここでカリー化! func = curry.call(10) # このような呼び出し方を「部分適用」と言います。 puts func.call(0) puts func.call(5) puts func.call(10) puts func.call(99) puts func.call(9999) [/sourcecode] |
いかがでしたでしょうか。
Rubyのクロージャにはカリー化するメソッドがついているんですねー。
「カリー化…?カレーライスになるの?」なんて寒いギャグを言っている暇があったら
カリー化したほうが良い関数をどんどんカリー化して美しいコードを書いていきましょう!