こんにちは! フリーエンジニアの長瀬です。
Rubyをお使いの方で、普段yieldを活用されていらっしゃる方はどのくらいいらっしゃるでしょうか?
名前だけはなんとなく聞いたことがあるけれど、人に説明しようとしたとき言葉に出なかったり、具体的なコードの書き方や引数については理解していないので、知ってるけど使わない、そんな状態になっていたりはしないでしょうか。
この記事ではrubyのyieldについて
・yieldとは
・yieldの使い方
という基本的な内容から、
・yieldの意味
・yieldに引数とブロック引数を同時に渡す
といった応用的な内容についても解説していきます。
yield基本編
yieldとは
yieldという英単語を日本語に訳すと「産する、もたらす、生む、引き起こす」など、いろいろな意味があります。
Rubyにおけるyieldの意味としてしっくりくるのは「他のものに取って代わられる」でしょう。
そんなyieldを一言で説明すると何と表現できるでしょうか。
一般的にRubyのメソッド引数には、数値や文字列など単体のオブジェクトを渡すこともできるのですが、実はブロックというものを渡すこともできます。
ブロックとは「メソッド呼び出しの際に引数と一緒に渡すことのできる処理のかたまり」です。
yieldは「渡されているブロックと同じ働きをするメソッドのようなもの」とも言えるでしょう。
これだけですとyieldの意味・価値、使い方がイメージしづらいと思いますので、以降でサンプルコードを交えながら説明していきます。
yieldの使い方
実際にコード上でyieldを使うとしたらどのような使い方をするのかを見ていきましょう。下記がサンプルコードです。
def test yield end test{puts "hoge"}
実行結果
hoge
とても単純なコードですね。
testメソッドを呼び出すと、呼び出したときに渡されたブロック(この場合は「puts “hoge”」)がyieldによって呼び出されています。
yieldの意味
さきほどのサンプルコードでは当然のように、ブロック引数をfooに渡していますが、実はrubyではすべてのメソッドがデフォルトでブロック引数を受け取れるようになっています。
だから、特になにも引数に指定しなくてもブロックを渡すことができたのです。
ブロック引数とはその名前のとおり、メソッドでブロックを受け取るための引数のことです。(そのままですね)
ちなみに念を押しますが、rubyでは{}やdo~endで囲まれた部分のことをブロックといいます。
ここでの解説はブロックについては理解しているものとして話を進めているのです、理解が浅いと思った人は以下の記事を参考にしてみてください。
つまり、さきほど紹介したサンプルコードではブロック引数を渡しています。
なので、省略せずに書くと以下のようになります。
def foo(&proc) yield end
&procでブロック引数をとることを明示しています。
ここで、
block(ブロック)といっているのになんでprocなんですか?
と思った方はセンスありです。
そうです、rubyの世界で存在するものはすべてがオブジェクトであり、ブロックはオブジェクトではないのでブロック単体では存在を維持することができません。
そこで、ブロックをProcオブジェクトとして扱うことによって、初めてブロックは引数として受け渡しが可能になります。
Procについてはこちらの記事で説明していますので、理解が浅いと思った方は参考にしてみてください。
つまり、ブロックだけでは力不足なのでProcオブジェクトの力を借りるというようなイメージです。
このように、実はyieldは受け取ったブロック引数を展開するためのメソッドなのです。
次に、ブロック引数のなぞが解けたところで、yieldそのものが意味するところに迫っていきましょう。
さきほどのサンプルコードはさらに省略せずに書くことが可能です。
def foo(&proc) proc.call end
Procオブジェクトはcallメソッドで、ブロックを展開できます。
勘のいい人はもうお気づきかと思いますが、yieldとは実はProcオブジェクトとして存在するブロックを展開するという意味だったのです。
一番はじめの例をyieldを使わずに書いてみます。
def foo(&proc) proc.call end foo(){puts "hoge"}
実行結果
hoge
yield本来の姿に置き換えただけですので、問題なく動きますね。
yieldの謎が解けて、すっきりしました。
yield応用編
yieldに引数とブロック引数を同時に渡す
では次にyieldに普通の引数とブロック引数を同時に渡す方法を見ていきます。
以下がサンプルコードです。
def foo( x ) yield if block_given? return x + 2 end p foo( 3 ) p foo( 5 ){ p "foo" }
実行結果
5 "foo" 7
2回目のfooメソッド呼び出し時にブロック「p “foo”」を渡すことでyieldによりそのブロックが実行されています。
block_given?はもしもブロック引数が渡されているのならtrueを返すメソッドです。
なので、後置ifで if block_give?としておけば、ブロックが渡されないときでもyieldを無視できます。
if文の使い方はこちらを参考にしてください。
こうしておかないと、もしもブロック引数が存在しない場合にはエラーとなってしまうのでブロックを引数が必ず必要なメソッドでない限り記述する必要があります。
また、さきほど同様にyieldを使わずに書き直すと以下のとおりです。
def foo( x, &proc ) proc.call if block_given? return x + 2 end p foo( 3 ) p foo( 5 ){ p "foo" }
実行結果
5 "foo" 7
問題なく動きます。
まとめ
今回の記事では、Rubyにおけるyieldについてまとめました。
yieldは省略の結果生まれたものなので、必ずyield本来の姿をイメージできるようにしておいてください。
yieldの中身がわかっていれば、yieldと書くだけでブロック引数を返してくれるので、とても便利ですね。
もしyieldについて忘れてしまったらこの記事を確認してくださいね!