LoginSignup
2
1

More than 3 years have passed since last update.

[1, 2, 3].map(&:to_s) という書き方のカラクリについて

Last updated at Posted at 2019-07-05

[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
2
1
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1