/ ,' 3

https://twitter.com/gorlemkun

メソッドのブロック引数、&(アンパサンド)によるProc展開と他オブジェクトの強制Proc変換

Rubyの話。たしかRuby1.9以降なら通用するはず。

ブロック引数付きのメソッドは通常メソッドのおしりにブロックをくっつけて実行する。

irb(main):001:0> def block_method(n)
irb(main):002:1>   i = 1
irb(main):003:1>   while(i <= n)
irb(main):004:2>     yield "#{i} times"
irb(main):005:2>     i += 1
irb(main):006:2>   end
irb(main):007:1> end
=> :block_method
irb(main):008:0> block_method(3) {|x| puts x}
1 times
2 times
3 times
=> nil

Proc.newでブロックをProcオブジェクト化した場合は、メソッドの引数の最後に&付きで渡すと実行できる。

irb(main):009:0> proc = Proc.new {|x| puts x}
irb(main):010:0> block_method(3, &proc)
1 times
2 times
3 times
=> nil

&つけないと、メソッド引数の数がおかしいことになるのでArgumentErrorとなる。

irb(main):011:0> block_method(3, proc)
ArgumentError: wrong number of arguments (given 2, expected 1)

では&をつけた状態でProc以外を渡すとどうなるのか・・・?この時は、渡したオブジェクトに#to_procが定義されていれば、強制的にProcに型変換されて実行される。

例えば、シンボルを&付きでイテレータに渡すと、Symbol#to_procが実行され、シンボルが同名のメソッドを使う手続きオブジェクトに変わってしまう。こうすることで、例えばイテレータのブロック引数に任意のメソッドを実行していける。

irb(main):001:0> words = ['foo', 'bar', 'baz']
=> ["foo", "bar", "baz"]
irb(main):002:0> uppercase = words.map &:upcase
=> ["FOO", "BAR", "BAZ"]
# 下記のブロックを渡した時と同じ挙動になる
irb(main):003:0> words
=> ["foo", "bar", "baz"]
irb(main):004:0> upper = words.map {|word| word.upcase}
=> ["FOO", "BAR", "BAZ"]

なんだかすんなり理解できなかったので、、