Hatena::ブログ(Diary)

わからん

2012.04.15

[] Symbol#to_proc でワンコールブロックを簡潔に書こう

Ruby の foo.map(&:bar) といったコードが、どういう仕組みで、どういうときに使えるのかを解説します。


 使用例

Symbol#to_proc はワンコールブロック(処理が一行しかないブロック)を簡潔に書くのに使われています。


これが、

names = ['bob','bill','mike']
names.map {|name| name.capitalize}   # => ["Bob", "Bill", "Mike"]

次のように、簡潔に書けます。

names = ['bob','bill','mike']
names.map(&:capitalize)              # => ["Bob", "Bill", "Mike"]

ひとことでいうと、「Symbol が Proc に変換され、Proc がブロックに変換されるから」上記のような記述が可能になります。


 解説

Symbol には、to_proc メソッドが定義されているため、:foo.to_proc で Symbol を Proc に変換できます。


▼ 実際よりもかんたんな、実装のイメージ

class Symbol
  def to_proc
    Proc.new {|x| x.send self}
  end
end

:foo.to_proc は Proc.new { |arg| arg.foo } のようなものだとわかります。


メソッド呼び出しの引数の Proc の前に & を付けると、Proc はブロックに変換されます。

def my_method
  yield if block_given?
end

my_proc = Proc.new {'AAA'}
my_method(&my_proc)           # => "AAA"

Ruby は、メソッド呼び出しの引数の頭に & がついていれば、&foo の foo が Proc であることを期待しているようです。

もし、&foo の foo が Proc でないなら、Proc に変換できないか試みるようです。

すなわち、ためしに foo の to_proc を呼び出しています。

だから、foo が Symbol なら、Symbol#to_proc が定義されているため、都合よく Proc に変換され、

その結果がブロックに変換されるのです。


段階的に書き並べてみました。もうどうしてこれでよいのか、理解できますよね。

names = ['bob','bill','mike']
names.map {|name| name.capitalize}   # => ["Bob", "Bill", "Mike"]
names.map(&(:capitalize.to_proc))    # => ["Bob", "Bill", "Mike"]
names.map(&:capitalize.to_proc)      # => ["Bob", "Bill", "Mike"]
names.map(&:capitalize)              # => ["Bob", "Bill", "Mike"]

以上、「メタプログラミング Ruby」の 付録A.5 Symbol#to_proc() を、書籍より初級者向けに解説してみました。

 

はてなユーザーのみコメントできます。はてなへログインもしくは新規登録をおこなってください。

Google