自己紹介
はじめまして。にっしーといいます。
私は4月14日から、山浦 清透さんが主催するアプレンティスの9期生としてWebエンジニアになるべく日々勉強を行っています!アプレンティスシップの課題や学習中に気になったことを投稿していたらと思っています!
アプレンティスについて、気になる方は👇のリンクから概要等見てみてください!
はじめに
Ruby関連のコードを書いたり、記事を読んだりしているとちょこちょこ出てくる(&:メソッド名)
について、「こういう書き方もできるんだ〜」くらいに思っている方も多いのではないでしょうか?
['hoge','fuga','piyo'].each(&:upcase!)
=> ["HOGE", "FUGA", "PIYO"]
私自身も、Rubocop(自動でコードの修正案を出してくれるやつ)に👇の画像のように指摘を受けたことはありましたが、原理等は気にせずコードだけ修正してしまったことがあります💦
いろいろ調べてみた
調べてみた結果、Rubyのさまざまな要素を組み合わせることで、成り立っている構文であることがわかった。
大まかな要素は👇の通りで、それぞれ深掘りしていく!
1.Enumerableモジュールのメソッド
2.&演算子
3.procオブジェクト
4.Symboleクラスのto_procメソッド
Enumerableモジュールのメソッド
Rubyでよく使うeach
やmap
などは、Enumerableモジュールに定義されたメソッド群です。
これらのメソッドはブロック引数を受け取って、繰り返し処理や変換処理を行うことができます。
→この要素で大事なことはブロック引数を受け取ることです!
&演算子
Rubyの&演算子
には様々な役割があるのですが、今回はProcオブジェクトをブロックに変換する用途で使用している!
公式ドキュメント参照
xxx(&b)
Proc オブジェクトをブロックとして使う。メソッド呼び出し(super・ブロック付き・yield)/ブロック付きメソッド呼び出し を参照。
to_proc メソッドを持つオブジェクトならば、`&' 修飾した引数として渡すことができます。デフォルトで Proc、Method オブジェクトは共に to_proc メソッドを持ちます。to_proc はメソッド呼び出し時に実行され、Proc オブジェクトを返すことが期待されます。
→&演算子を使うと、ブロック引数の代わりにProcオブジェクトを渡すことができる!
さらに、to_proc
メソッドが定義されている場合、そのオブジェクトをto_proc
メソッドでProcオブジェクトに変換し、ブロック引数として渡すことができる!
Procオブジェクト
ブロック(処理をひとまとまりにしたもの)をあらかじめ定義し、変数として保存できるオブジェクトです。
よく使う処理をあらかじめ変数に保存しておくことで、さまざまな場所で繰り返し呼び出して利用することができる!
公式ドキュメント参照
ブロックの部分だけを先に定義して変数に保存しておき、後からブロック付きメソッドに渡すことも出来ます。 それを実現するのが手続きオブジェクト(Proc)です。それをブロックとして渡すにはブロック付きメソッドの最後の引数として `&' で修飾した手続きオブジェクトを渡します。
Procオブジェクト・&演算子使ってみた
# 処理をあらかじめProcオブジェクトとして作成し、変数output_strに保存
output_str = proc { |output| puts output }
#Procオブジェクトを&演算子でブロックに変換して、eachメソッドに渡す
['hoge','fuga','piyo'].each(&output_str)
=> hoge
fuga
piyo
→Procオブジェクトは処理をまとめて、&演算子をつけることでブロック引数として実行することができる!
Symbolクラスのto_procメソッド
これまでの説明だけだと、
「実際は、Procオブジェクトではなくただのシンボルを渡しているのに、なぜ実行されるの!?」
と思うかもしれません。
その理由は、Symbolクラスがto_proc
メソッドを持っているからです!
Symbol#to_proc
は、呼び出したシンボルと同じ名前のメソッドを、引数に対して呼び出すProcオブジェクトを作成する機能を持っています。
これにより、たとえば次のようなコードが動くわけです。
# :upcaseをto_procで変換することでupcaseメソッドを実行するProcオブジェクトが作成される。
:upcase.to_proc.call("hoge")
=> HOGE
→このように、シンボルを渡しているように見えて、内部ではto_procによってブロック(Procオブジェクト)に変換され、それがブロックとして実行されているのです!
まとめ
&:メソッド名
を渡した際は、👇のような流れで変換されてから、実行されます。
1.&演算子
が:メソッド名
をProcオブジェクトに変換する。
2.Procオブジェクトをブロックとしてメソッドに渡す。
3.受け取ったブロック引数を使ってメソッドを実行する。
おわりに
今回、初めて技術投稿の記事を書いてみました。
内容の修正などご指摘等ございましたらコメントいただけたら幸いです。
参考
・公式ドキュメント
Enumerableモジュール
&演算子
Symbol#to_proc
・書籍
プロを目指す人のためのRuby入門[改訂2版] 言語仕様からテスト駆動開発・デバッグ技法まで