12
1

[Ruby] メソッドとSymbolとProcの関係について

Last updated at Posted at 2023-12-08

はじめに

Railsを用いた開発を進めている中で、ブロックを渡すはずのメソッドにシンボル形式のメソッドを渡している処理を見かけました。

今までも同様の記述は見たことがあったのですが、なぜシンボル形式のメソッドを使用するのかよく理解していなかったので、この機会にメソッド・Symbol・Procについて調べてみました。

Enumerable#collectを使用した例

Rubyリファレンスマニュアルには下記のようなcollectメソッドの使用例が記載されています。

p (1..3).collect { "cat" } # => ["cat", "cat", "cat"]

各要素に対してブロックを評価した結果を全て含む配列を返します。

上記の例だと(1..3)の各要素に対して{ "cat" }を適用するため、戻り値が["cat", "cat", "cat"]となるわけです。

一方で、ブロックの記法を用いず、シンボル形式のメソッドを渡す書き方もあります。

p (1..3).collect(&:to_s) # => ["1", "2", "3"]

本来ならばcollectメソッドにはブロックを渡さなければいけないにも関わらず、上記の例ではシンボルを渡しているように見えます。

そこで、

ブロックを渡すはずのメソッドになぜシンボル形式のメソッドを渡すことができるのか

メソッドをシンボルの形で渡すのはなぜなのか

という2点に着目して調査をしてみました。

to_procメソッドとの出会い

調査を続けていると、一つのメソッドに遭遇しました。
それがto_procメソッドです。
Rubyリファレンスマニュアルには下記のように説明されています。

self に対応する Proc オブジェクトを返します。
生成される Proc オブジェクトを呼びだす(Proc#call)と、 Proc#callの第一引数をレシーバとして、 self という名前のメソッドを残りの引数を渡して呼びだします。
生成される Proc オブジェクトは lambda です。

下記のように使用します。

# 明示的に呼ぶ例

:to_i.to_proc["ff", 16]  # => 255 ← "ff".to_i(16)と同じ
# 暗示的に呼ぶ例

# メソッドに & とともにシンボルを渡すと
# to_proc が呼ばれて Proc 化され、
# それがブロックとして渡される。
(1..3).collect(&:to_s)  # => ["1", "2", "3"]
(1..3).select(&:odd?)   # => [1, 3]

つまり...

  • Symbolで記述されたメソッドはProcオブジェクトに変換可能である。
  • 引数に&とともにSymbolを渡す事で、メソッドをブロックとして渡すことができる。

ということがわかりました。

実験

ここまでの内容を噛みくだいて理解するため、下記のようなサンプルコードでto_sを色々な形で出力してみました。

puts to_s.class
# => String
#    to_sメソッドの戻り値のクラスを調べている.

puts :to_s.class
# => Symbol
#    to_sメソッドの情報を持ったSymbol

puts :to_s.to_proc.class
# => Proc
#    to_sメソッドをブロックとして持つProcオブジェクト

puts :to_s.to_proc[100].class
# => String
#    Procを実行した結果の戻り値なのでString(今回だと'100')が返される

単にto_sメソッドを呼び出した場合はメソッドの戻り値であるStringオブジェクトが返ってきます(当たり前ですが...)。

一方で:to_s:to_s.to_procでは、それぞれSymbolオブジェクトやProcオブジェクトを返しています。
to_sの実行結果の戻り値ではなくto_sメソッドをSymbolやProcの形式で扱うことができていることがわかります。

また、興味本位でメソッド定義部分自体のクラスも調べてみました。

puts def hoge
      'hoge'
     end.class
# => Symbol

この段階ですでにメソッドはSymbolオブジェクトになっているのですね。

まとめ

前述の疑問点に対して私が出した答えは以下です。

ブロックを渡すはずのメソッドになぜシンボル形式のメソッドを渡すことができるのか

  • シンボルはto_procメソッドを持っているため、シンボルが持つメソッドの情報をProcオブジェクトに変換してブロックを実行することが可能である。

メソッドをシンボルの形で渡すのはなぜなのか

  • メソッドを&とともにシンボルの形で引数に渡すことにより、メソッドの戻り値ではなくブロックを渡すことができる。

参考

12
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
1