Ruby

【Ruby】array.map(&:method)を理解する


概要

Rubyで書かれたコードを見ていると度々array.map(&:メソッド名)の形式のイディオムを見かけると思います。そこで、array.map(&:メソッド名)がどのような動きをし、またなぜそのような動きをするのかまとめました。


何をする?

簡単にいうと以下のように配列の要素1つ1つに(&:メソッド名) で与えられたメソッドを呼んでいます。

(以下の例では文字列のabに対してString#upcaseメソッドを呼んでいます。

['a' ,'b'].map(&:upcase) #=> ["A", "B"]


なぜこのような結果になる?


引数にある&

まず、Rubyが引数内で&を見つけるとその値をプロック(proc)として扱おうとし、その値(この場合:upcase)に対してto_propcメソッドを呼びます。

class Symbol

def to_proc
puts "to_procが呼ばれました!"
end
end

p [].map(&:upcase) #=> to_procが呼ばれました!
## その後TypeErrorが発生。


Symbol#to_procメソッドとは?

to_procメソッドはその名の通り、渡されたシンボルを手続きオブジェクトのprocとして返すものです。

prc = :upcase.to_proc 

p prc #=> #<Proc:0x00007f99d303e458(&:upcase)>

作成したprcを文字列に呼んでみます。

p prc.call("string") #=> STRING

まとめると以下の事を行なっています。

:upcase.to_proc.call("string")#=> STRING


結論

つまり、array.map(&:メソッド名)(&:メソッド名)部分は:メソッド名.to_proc.callを省略した形という事になります。

以下の例で実際に両者の比較をみてみます。

# array.map(&:メソッド名)の記法

['a','b'].map(&:upcase) #=> ["A", "B"]

# 省略しない記法
['a','b'].map do |string|
:upcase.to_proc.call(string)
end
#=> ["A", "B"]


参考

Understanding Ruby's idiom: array.map(&:method)

https://therealadam.com/2008/05/01/beautiful-multi-line-blocks-in-ruby/