Help us understand the problem. What is going on with this article?

Hash#to_proc と Method#to_proc

More than 1 year has passed since last update.

はじめに

この記事は書籍「プロを目指す人のための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のカリー化について説明します。

jnchito
SIer、社内SEを経て、ソニックガーデンに合流したプログラマ。 「プロを目指す人のためのRuby入門」の著者。 http://gihyo.jp/book/2017/978-4-7741-9397-7 および「Everyday Rails - RSpecによるRailsテスト入門」の翻訳者。 https://leanpub.com/everydayrailsrspec-jp
https://blog.jnito.com/
sonicgarden
「お客様に無駄遣いをさせない受託開発」と「習慣を変えるソフトウェアのサービス」に取り組んでいるソフトウェア企業
http://www.sonicgarden.jp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした