Ruby
プロを目指す人のためのRuby入門

はじめに

この記事は書籍「プロを目指す人のためのRuby入門」に掲載できなかったトピックを著者自らが紹介するアドベントカレンダーの22日目です。
本文に出てくる章番号や項番号は書籍の中で使われている番号です。

今回はHash#to_procMethod#to_procを紹介します。

必要な前提知識

「プロを目指す人のためのRuby入門」の第10章まで読み終わっていること。

Hash#to_proc と Method#to_proc

10.5.2項ではシンボルがto_procメソッドを持つことを説明しました。to_procメソッドを持つオブジェクトは他にも存在します。ここではハッシュのto_procメソッドとMethodクラスのto_procメソッドを紹介します。

ハッシュの場合

Ruby 2.3からはハッシュもto_procメソッドを持つようになりました。ハッシュをProcオブジェクトに変換すると、実行時に第1引数に渡された値が元のハッシュのキーに一致した場合にハッシュの値を返します。一致するものがなければnilが返ります。

hash = { japan: 'yen', us: 'dollar', india: 'rupee' }

# to_procを使わない場合
without_to_proc = proc { |key| hash[key] }
without_to_proc.call(:japan)
#=> "yen"
without_to_proc.call(:italy)
#=> nil

# to_procを使う場合
with_to_proc = hash.to_proc
with_to_proc.call(:japan)
#=> "yen"
with_to_proc.call(:italy)
#=> nil

これにより、キーの配列からハッシュの値をマッピングしたりする際に簡潔に書けるようになります。

hash = { japan: 'yen', us: 'dollar', india: 'rupee' }

[:japan, :india, :italy].map { |country| hash[country] }
#=> ["yen", "rupee", nil]

[:japan, :india, :italy].map(&hash)
#=> ["yen", "rupee", nil]

ちなみに、上のコードはvalues_atメソッドを使っても同じ結果が得られます。(ただし、メソッドのレシーバがキーの配列ではなく、ハッシュになります。)

hash.values_at(:japan, :india, :italy)
#=> ["yen", "rupee", nil]

Methodオブジェクトの場合

Rubyではメソッドの定義についてもMethodオブジェクトという形でオブジェクト化することができます。たとえば以下はshuffleというメソッドを定義し、それをMethodオブジェクトとして取得するコード例です。

def shuffle(str)
  str.chars.shuffle.join
end

method_shuffle = self.method(:shuffle)
#=> #<Method: Object#shuffle>

Methodオブジェクトもto_procメソッドでProcオブジェクトに変換することができます。実行時の引数はそのまま元のメソッドの引数になり、元のメソッドが実行されます。

proc_shuffle = method_shuffle.to_proc
proc_shuffle.call('Ruby')
#=> "bRyu"

これを利用すると、&とMethodオブジェクトを組み合わせてブロックを利用するメソッドの引数にすることも可能です。

def shuffle(str)
  str.chars.shuffle.join
end

# ブロックの中で普通にメソッドを呼び出す場合
['Ruby', 'Java', 'Perl'].map { |name| shuffle(name) }
#=> ["uybR", "Jvaa", "elrP"]

# &とMethodオブジェクトを使う場合
['Ruby', 'Java', 'Perl'].map(&method(:shuffle))
#=> ["ybRu", "aJva", "Prle"]

コードとしては少しトリッキーなので、日常的に使うことはあまりおすすめできませんが、Procオブジェクトの面白い利用例の1つと言えそうです。

次回予告

次回はProcのカリー化について説明します。