色々読んで混乱したので、自分が理解するためにできるだけ一次ソースを拾いながらまとめてみました。
間違っている点等あれば、教えていただけますと助かりますm(_ _)m
まとめ
&をつけて渡したオブジェクト(:to_i)のto_proc
メソッドが暗黙的に実行され、
Procオブジェクトを返し、最終的にはよく見る形になって実行されている。
["1", "2", "3"].map{ |obj| obj.to_i }
下記の結果は同じ。
なんでこうなるの?
["1", "2", "3"].map{|v| v.to_i}
=> [1, 2, 3]
["1", "2", "3"].map(&:to_i)
=> [1, 2, 3]
&は何をしているか。
mapメソッドはブロック付きメソッド。
ブロック付きメソッドの引数には、ブロックを渡す必要(※1)があるが
to_procメソッドを持つオブジェクトの前に、&をつけることでmapメソッドの引数としても渡すことができる。
つまり、&はto_procメソッドを持つオブジェクトを引数として渡している。
to_proc メソッドを持つオブジェクトならば、`&' 修飾した引数として渡すことができます。
メソッド呼び出し(super・ブロック付き・yield) (Ruby 1.9.3)
(※1)
ブロック付きメソッドの引数にブロックを入れた変数を渡すと、ブロックではなく引数として認識されエラーになる例。
(つまり、ブロック付きメソッドにはブロックを渡す必要がある。)
blok = Proc.new {|v| v.to_i} # ブロックをオブジェクト化したもの。オブジェクト化しないとエラーになるため。
["1", "2", "3"].map blok
=> ArgumentError: wrong number of arguments (given 1, expected 0)
to_procメソッドを持つオブジェクトとは何か。
:to_i
のこと。
# :to_iは、Symbolクラス。
:to_i.class
=> Symbol
# Symbolのインスタンスは、to_procメソッドを持っている。
:to_i.method(:to_proc)
=> #<Method: Symbol#to_proc>
&をつけて、to_procメソッドを持つオブジェクト(:to_i)を引数に渡していることがわかったけど、どういうこと??
じつは、&をつけてto_procメソッドを持つオブジェクトを引数に渡したときには続きがありまして、
暗黙的にto_procメソッドを持つオブジェクトのto_proc
メソッドを試みるそうです。
to_proc メソッドを持つオブジェクトならば、`&' 修飾した引数として渡すことができます。デフォルトで Proc、Method オブジェ クトは共に to_proc メソッドを持ちます。to_proc はメソッド呼び出し時に実 行され、Proc オブジェクトを返すことが期待されます。
メソッド呼び出し(super・ブロック付き・yield) (Ruby 1.9.3)
つまり、.to_procが暗黙的に実行されています。
["1", "2", "3"].map(&:to_i.to_proc)
=> [1, 2, 3]
to_procは何か。
ちょっと古いですが、わかりやすさを優先してピックアップ。
下記の通りです。
Symbolクラスのインスタンス(つまりSymbolオブジェクト)に対してto_procメソッドが実行されると、「Proc.new { |obj, *args| obj.send(self, *args) }」というProcオブジェクトを返す、という挙動をしているよ。
ActiveSupport(2) - Symbol#to_proc - バリケンのRuby日記 - Rubyist
to_procが実行されると、Proc.new { |obj, *args| obj.send(self, *args) }
というProcオブジェクトが返ってきます。
to_procはインスタンスメソッドなので、to_procが実行されたときに返ってくるProc.new { |obj, *args| obj.send(self, *args) }
のselfは:to_iになるので、
["1", "2", "3"].map{ |obj, *args| obj.send(:to_i, *args) }
となる。ちょっと、いつもと見慣れた形になってきました。
ここでは引数をひとつしか取らないから、下記になるそう(ここがなぜかはわからない。。教えてほしい。。)。
["1", "2", "3"].map{ |obj| obj.send(:to_i) }
sendメソッドは、引数で与えられた文字列またはシンボルに対応するメソッドを呼び出すので、
["1", "2", "3"].map{ |obj| obj.to_i }
となる。
参考
色々参考にさせていただきましたが、下記が一番わかりやすかったです。