[1, 2, 3].map(&:to_s) という書き方
1.8.7 からできるらしい。
[1, 2, 3].map(&:succ) #=> 2, 3, 4
[1, 2, 3].map { |i| i.succ } #=> 2, 3, 4
なぜこの記述ができるのか?
これを説明するには、以下に述べる 2つの仕組みを理解しておく必要がある。という話。
Symbol#to_proc
Symbol#to_proc は Proc オブジェクトを返す
- 第一引数のオブジェクトをレシーバにして、自身の名前のメソッドを残りの引数を渡して呼び出す
- ちなみに lambda ではない (
.labmda? #=> false
)
:to_s.to_proc
は例えば proc { |*args| args.shift.public_send(self, *args) }
のような(※) proc になる
※ 実際に public_send
が呼ばれるわけではなく自前で記述する場合の例示です
proc1 = proc { |x| x.to_s }
proc2 = proc { |*args| args.shift.public_send(:to_s, *args) }
proc3 = :to_s.to_proc
proc1.call(5) #=>"5"
proc2.call(5) #=>"5"
proc3.call(5) #=>"5"
ブロックを渡すために & を使う
(これ、正式な呼び方あるのだろうか?)
ブロックを渡す箇所で代わりに &
を頭につけてオブジェクトを渡すと、proc オブジェクトをブロックとして渡すことができる。
plus_two = proc { |x| x + 2 }
[1, 2, 3].map(&plus_two) #=> [3, 4, 5]
上記で「proc オブジェクトを」としているが、正確には to_proc
に反応するオブジェクトであればなんでも良い。
class PlusTwo
def to_proc
proc { |x| x + 2 }
end
end
[2, 3, 4].map(&PlusTwo.new) #=> [4, 5, 6]
module PlusThree
module_function def to_proc
proc { |x| x + 3 }
end
end
[2, 3, 4].map(&PlusThree) #=> [5, 6, 7]
なお、 Proc オブジェクト自体も to_proc
に反応する。その結果として self
を返す。
plus_two = proc { |x| x + 2 }
plus_two == plus_two.to_proc #=> true