この広告は、90日以上更新していないブログに表示しています。
この記事は、Rubyを書くにあたって「いかにブロックを書かずにすませるか」を追求した、誰得な連載である。
だって、ブロックって汚いじゃん?仮引数の|x|とかキモイ。
基本中の基本。
instance method Symbol#to_proc
以下のようなArrayがあって、
irb(main):003:0> arr = [:user,:entry,:article,:comment,:category]=> [:user,:entry,:article,:comment,:category]
各要素をto_sしたかったら、
irb(main):004:0> arr.map{|s| s.to_s}=> ["user","entry","article","comment","category"]
と書くかわりに、
irb(main):005:0> arr.map(&:to_s)=> ["user","entry","article","comment","category"]
って書けばいい。
これは、ブロックを受け取るメソッドにProc以外が渡されると、to_procを呼び出して型変換を行う仕組みによる。
to_proc メソッドを持つオブジェクトならば、`&' 修飾した引数として渡すことができます。デフォルトで Proc、Method オブジェ クトは共に to_proc メソッドを持ちます。to_proc はメソッド呼び出し時に実 行され、Proc オブジェクトを返すことが期待されます。
メソッド呼び出し(super・ブロック付き・yield)
Symbol#to_procの実装は、こんなイメージ。ようは、引数に対してsendでSymbolを送るprocを生成する
defto_procproc{|obj, *args| obj.send(self, *arg)}end
このようになる。
irb(main):006:0>:to_s.to_proc=>#<Proc:0x007fcf721c21d8>irb(main):007:0>:to_s.to_proc.call(:user)=>"user"
Object#methodは大変素晴らしいデス。これ無くして関数型Rubyは語れない。
Object#methodは、レシーバーとなったObjectから、Methodオブジェクトを取り出す。
このMethodオブジェクトから、メソッド名をレシーバーに対して呼び出すProcを作り出すことができる。
irb(main):010:0> method = arr.method(:size)=>#<Method: Array#length>irb(main):011:0> method.call=>3irb(main):012:0> method.to_proc.call=>3
この例では、arrオブジェクトに対するsizeメソッド呼び出しを、Procとして取り出している。
Procとして取り出せるということは、特定のオブジェクトへのメソッド呼び出しがProcという形で可搬性を得ているといえる。
つまり、一度Procにしてしまえば、変数に代入しようが他の関数に引数として渡そうが、やりたい放題絶頂ってワケ。ビバ!ファーストクラス!!
このアプローチで、select, map, injectなどのブロックを取る高階関数群を、ブロックを書くこと無く利用できる。
具体例を示そう。
classCriteriadefinitialize(length)@length = lengthenddefallow?(s) s.length >=@lengthendend
このCriteriaクラスは、allowメソッドで、@lengthでもっている値と引数の値を比較する。
今、lengthが6のCriteriaオブジェクトがあり、それに対してallow?がtrueを返すものだけarrの中から抽出したい。
ブロックを使う場合は、こうなる。
irb(main):053:0> criteria =Criteria.new(6)=>#<Criteria:0x007fcf71031f90 @length=6>irb(main):054:0> arr.select{|s| criteria.allow? s }=> [:article,:comment,:category]
Object#methodを利用すれば、こう書ける。
irb(main):055:0> arr.select(&criteria.method(:allow?))=> [:article,:comment,:category]
criteriaオブジェクトから、allow?メソッドへの呼び出しをObject#methodで取り出して、selectに渡せばよいだけだ。
Symbol#to_procやObject#methodを使えば、単なるメソッドを呼び出しを行うためだけにわざわざブロックを書かなくてもよくなる。
もちろん、このスタイルが読みやすいコードであるかは賛否両論あるだろうが、ファースクラスな関数の可搬性を意識することでDRYなコードを実現する一助とはなるであろう。
次回は関数合成について書く。( ;゚皿゚)ノシΣ フィンギィィーーッ!!!
引用をストックしました
引用するにはまずログインしてください
引用をストックできませんでした。再度お試しください
限定公開記事のため引用できません。