RubyのMethodクラスの公式ドキュメントを読んでいた所、面白いメソッドを発見しました。
なんとメソッドをカリー化してProcにした値を返してくれるcurry
というメソッドがありました。
Rubyではカリー化が簡単には出来ないと思っていたのですが、こんなメソッドが提供されていたとは...。
カリー化は可能だが、関数からProcを返すようにする必要があり面倒...
class Sample def self.add(n) Proc.new { |m| n + m } end end f = Sample.add(1) puts f.class # Proc puts f.call(1) # 2
curryメソッドを使ってみる
先ほどはadd(n)
と定義しましたがadd(n, m)
のままcurry
メソッドを使ってカリー化してみます。
合わせてカリー化されたProcを返すcurry_add
関数を定義しました。
class Sample def self.add(n, m) n + m end def self.curry_add self.method(:add).curry end end
さて、上手くカリー化されているでしょうか。
f = Sample.curry_add puts f # #<Proc:0x00001462f13371c0 (lambda)> puts f.call(1) # #<Proc:0x00001462f1336ab8 (lambda)> puts f.call(1).call(1) # 2
f
にはProcが束縛されています。
さらにf
に対してcallメソッドを呼び出してみると別のProcが返ってくることが分かります。
最終的にf
に対して2回、callメソッドを呼び出すことで数値を得ることが出来ました。
他の値でも実行可能かどうか、試してみます。
g = Sample.curry_add add_10 = g.call(10) puts add_10.call(1) # 11 add_100 = g.call(100) puts add_100.call(1) # 101
無事にカリー化されているようです。
ついでに関数合成も
同じくMethodクラスに記載のあった関数合成(<<
)についても試してみようと思います。
f(x) << g(x)
と記述することで、なんとf(g(x))
を実現することができるそう。このメソッドについても全く知りませんでした。
class Sample def self.f(x) x * x end def self.g(x) x + x end end pipe = (Sample.method(:f) << Sample.method(:g)) puts pipe # #<Proc:0x000014efd3dbfe48 (lambda)> puts pipe.call(3) # 36
関数合成(<<
)を行うとProcが返ってきます。つまり、内部的には別の新しい関数をProcで作成しているようです。
作成した合成関数(Proc)は例の如くcallメソッドで呼び出すことが出来ました。
ちなみに>>
を呼び出すことで、逆向きの関数合成も可能です。順序が変わったことで結果が変化しました。
pipe = (Sample.method(:f) >> Sample.method(:g)) puts pipe # #<Proc:0x000014e42e8cfee8 (lambda)> puts pipe.call(3) # 18
いやー、本当に知らないことがたくさんあるんだなと思わされます。
また新しいレシピが増えたので個人的には大満足です。