#はじめに
rubyでmapメソッドを利用する際、
number ["1","2","3"]
number.map &:to_i
#=> [1, 2, 3]
もしくは、、、
alphabet = ["a","b","c"]
alphabet.map(&:upcase)
#=> ["A","B","C"]
このような処理がなされているのをみたことがないだろうか。
What is "&:" ???
となったので深堀ってみると面白かったのでここに書き留めておく。
#前提として
alphabet = ["a","b","c"]
alphabet.map(&:upcase)
#=> ["A","B","C"]
とは、
alphabet = ["a","b","c"]
alphabet.map{|s| s.upcase}
#=> ["A","B","C"]
もっと省略せずに書くと、
alphabet = ["a","b","c"]
alphabet.map do |s|
s.upcase
end
#=> ["A","B","C"]
となり、
これら上記3つは同じ処理結果となる。
そしてここからは、なぜ"&:"を利用できるのかということに注目していく。
#Proc(プロック)クラス
"&"のことに触れる前に理解しておかなければならないのが、このProcクラスについてだ。
{}やdo endで書いたブロックをオブジェクト化したものがProcオブジェクト。
つまり、
{|s| s.upcase}のようなブロックをオブジェクト化したもの。
alphabet = ["a","b","c"]
p = Proc.new {|s| s.upcase}
alphabet.map(&p)
#=> ["A","B","C"]
#&とは
&(アンパサンド)演算子
"&"には主に2つの働きがあります。
- メソッド呼び出しで使う"&"はオブジェクトをブロックとして与えるもの。
- メソッド定義で使う"&"はブロックを Proc オブジェクトとして受け取るもの。
上記の例で言えば、メソッド呼び出しに"&"が使われているので、
Procオブジェクト(p)は"&"によって、ブロックとして扱われたということになる。
#Symbol#to_procメソッド
to_procメソッドはその名の通り、
渡されたシンボルをProcオブジェクトとして返すもの。
prc = :upcase.to_proc
puts prc
#=> #<Proc:0x00007f99d303e458(&:upcase)>
これを文字列に呼んでみると、
puts prc.call("abc")
#=> ABC
これらの処理をまとめると、
:upcase.to_proc.call("abc")
#=> ABC
つまり
# array.map(&:メソッド名)の記法
["a","b","c"].map(&:upcase) #=> ["A","B","C"]
# 省略しない記法
alphabet = ["a","b","c"]
alphabet.map do |s|
:upcase.to_proc.call(s)
end
#=> ["A","B","C"]
つまりSymbolである:upcaseはto_procによってProc化されると、**callの第1引数がレシーバー(.の左側)、upcaseがメソッド名(.の右側)**になって実行されるようなProcオブジェクトが自動的に作られる。
よって:upcaseから作成されたProcのcallがmapから呼び出されると、第1引数として与えられる配列の各要素がレシーバーとなり.upcaseが呼び出される。
これがmap &:upcaseで各要素に対して.upcaseが呼ばれる理由なのだ。
#おわりに
"&"と":"にというたった2文字の中にこんなにもたくさんの意味や処理が隠れていたことには驚いた。
同時に、スクールでしっかり学んだと思えていたrubyには、
まだまだ知らない部分があるのだということを実感した。
参考記事
【Ruby】array.map(&:method)を理解する
https://qiita.com/k-penguin-sato/items/420d7487b28b5d58cac4
Ruby Procについて学ぶ
https://qiita.com/naotospace/items/0163d4f8803a63f5fb17
&演算子と、procと、Object#method について理解しなおす
https://qiita.com/kasei-san/items/0392097791d3a5998216
Rubyのmap &:to_iとはなんなのか
https://a-records.info/ruby-map-ampersand-colon-to_i/