# Ruby 1.8で任意のメソッドをカリー化するメソッドをつくる。 _published: 2010/09/10_ ![alt](http://b.hatena.ne.jp/entry/image/http://d.hatena.ne.jp/shunsuk/20100910/1284106109) 「辛いものが食べたい』と君が言ったから九月十日は、Ruby 1.8で任意のメソッドをカリー化するメソッドをつくる記念日。 - [カリー化 - Wikipedia](http://ja.wikipedia.org/wiki/%E3%82%AB%E3%83%AA%E3%83%BC%E5%8C%96) Haskellで関数を書くと、勝手にカリー化しています。 ```haskell Prelude> let test a b c = a + b * c Prelude> (((test 1) 2) 3) 7 ``` Rubyでやるなら、こうなればいいわけです。 ```ruby def test(a, b, c) a + b * c end curried_test = method(:test).curry p curried_test.call(1).call(2).call(3) #=> 7 ``` 特定のメソッドをカリー化するなら、`lambda` をネストすればいいんですが。。任意のメソッドだと、ネストする階層を決定できません。文字列でコードをつくってevalすると簡単そうです。ですが、ここはあえて、`lambda` でなんとかしましょう。 再帰すればいいのですが、ここはあえてループで処理。内側から包んでいく感じで。引数の引渡しに悩みつつ。完成? ```ruby class Method def curry args = [] f = self self.arity.times do f = wrap(f, args) end f.call end def wrap(f, args) lambda { lambda {|x| args << x f.call *args } } end end ``` 正しく動くように見えましたが、呼び出しのたびに引数がどんどん増えていきます。そのため、エラーが発生。 ```ruby curried_test = method(:test).curry p curried_test.call(1).call(2).call(3) #=> 7 p curried_test.call(1).call(2).call(3) #=> ArgumentError ``` `args` のスコープが広すぎるんですね。呼び出しのたびに状態が変化しています。 副作用をなくします。 ```ruby class Method def curry args = [] f = self self.arity.times do f = wrap(f) end f.call end def wrap(f) lambda {|*args| lambda {|x| f.call *(args + [x]) } } end end ``` できたー。 ```ruby curried_test = method(:test).curry p curried_test.call(1).call(2).call(3) #=> 7 p curried_test.call(1).call(2).call(3) #=> 7 ``` ```ruby curried_test = method(:test).curry p f = curried_test.call(1) #=> <Proc:[email protected]:17> p f = f.call(2) #=> <Proc:[email protected]:17> p f.call(3) #=> 7 p f.call(4) #=> 9 ``` 今回は、Ruby 1.8とバージョンを限定しました。というのは、他の人の実装を見てみようと思い検索してみると、Ruby 1.9には `Proc` に `curry` メソッドが用意されていました。 ```ruby # Ruby 1.9 proc {|a, b, c| a + b * c}.curry[1][2][3] #=> 7 ``` ちなみに、Ruby 1.9では、 `.call(1)` の代わりに `1` と書くことができます。 Rubyの引数の `*` を活用した解法です。同じような書き方ができない言語が多いかもしれません。他の解法を考えてみてください。