【Ruby】ブロック付きメソッドとyieldについて

ABOUTこの記事をかいた人

気になること&知らないことをひたすらググることを得意としています。このブログでは最近気になったことを中心に記事を書いていきます。記事には間違った情報を書かないように細心の注意を払っていますが、もし誤りがあった場合はコメントやお問い合わせフォームからご指摘いただければ幸いです。

メソッドの定義でyieldを記述するとメソッドをブロック付きで実行した際にそのブロックをyieldの部分で実行されます。yieldの部分は定義したメソッドの外側で定義(呼び出しの際のブロックで表現)するのですこしややこしくなりますが、取り敢えずまずは下記のメソッドをみて挙動を確認して行きましょう。

ここでいうブロックとはメソッド実行時に「do~end」や「{~}」で囲まれているものです。
1
2
3

上記では「test_yield」メソッドを実行するとまず、2行目の「while true」にて無限ループに入ります。ですので「test_yield」メソッドを呼び出すときに「break」などで無限ループを抜ける処理を記述する必要があります。

処理の流れを確認

それでは上記のメソッドで処理の流れを見て行きましょう。

  • 7行目の記述で今回地味に重要となる「num = 1」を定義
  • 8行目でtest_yieldメソッドを実行
  • 「while true」で無限ループに突入
  • 3行目の「yield」で今回渡したブロックが実行
  • これで今回のブロック9~11行が繰り返し実行されることになる
  • 9行目のputs num で1が表示される
  • 10行目でnumに1を足して再度numに代入
  • 11行目ではnumが3を超えた場合にスクリプトが終了するがまだ1なので再度繰り返すことになる
  • 9~11行が3回繰り返されると11行目のif文がtrueとなりbreakが実行されメソッドの処理が終了。

もっと簡単なスクリプトをみてみる

処理内容を書いていってたら、これでは微妙だと思ったのでもっとシンプルなスクリプトを書いてみました。

yield
yield!!!

今回は繰り返しを省いたのでかなり見やすくなったかと思います。回りくどい書き方になりましたが、これで「test_yield」が呼ばれた際にブロックでの記述が渡されていることがわかります。

ブロック変数をつけてみる

次にブロック変数をメソッド呼び出しの際に記述して、処理を行う方法を見て行きましょう。ブロック変数は今回で言うと「|a|」の部分になります。

1
yield

test_yieldメソッド内で「num = 1」を定義し、yieldに引数として渡しています。yieldではブロックの処理を行う際にブロック変数にこの「num」の値が使えます。今回はブロック変数を「a」として「num」の値「1」が「a」に代入され、ブロック内でputsされることになります。

また、上記と同じ要領でブロック変数を増やしていくこともできます。

1
yieldって難しいね。

メソッド自体に引数を渡してみる

上記のスクリプトを少し変更して「test_yield」メソッド自体に引数を渡して、それを更に「yield」に渡し、ブロック内でその引数の処理を決める方法を見ていきます。

2
yieldって難しいね。
↓2回puts↓
yieldって難しいね。
yieldって難しいね。

8行目のtest_yieldメソッドの呼び出しはただ単にputsするだけのブロックを渡しましたが、12~17行目は少し複雑な感じになっています。

12行目で渡された引数は結局ブロック変数「a,b」に代入されて処理が行われています。ブロック内の記述の方がメソッドよりも比重が多くなってしまいました。もうもはやどちらがメソッドなのかわかりませんね。笑

1番初めのスクリプトと同様の処理をしてみる

結構まとまりがなくて申し訳ありませんが、再度1番初めに紹介したスクリプト「yield.rb」と似たような処理をブロック変数を用いて記述してみます。そして今回は処理のほとんどをメソッド側で定義してみました。

1
2
3

これでメソッド内でループ処理のほとんどが実行されるようになり、ブロックではブロック変数を用いての処理のみ記述することができるようになりました。今回はブロックの処理は3回行われて、|block_num|のブロック変数が「1、2、3」の三回変化することになります。

yieldの戻り値

いろいろと挙動を調べていくうちにyieldをメソッド内で実行するとその戻り値を取得できるということに気が付きました。また、yieldの戻り値はブロック内の記述の1番最後の値となるようです。

[1, 2, 3]
[2, 4, 6]

今回は3行目で変数「ary」に配列オブジェクトを新しく定義しました(Array.newと同じ意味)。そして5行目で実行した「yield」の結果を変数「a」に代入し、6行目で「push」メソッドを使い、要素を追加していっています。

block_given?メソッド

「block_given?」メソッドは記述したメソッドが呼び出された際にブロックが与えられていれば「true」与えられていなければ「false」を返します。

true
false

4行目ではブロックを渡したので「true」が返ってきて5行目ではなにも渡していないので「false」となります。これを利用してブロックを渡したときと渡さないときで挙動が違うメソッドを作ってみます。

[2, 3, 4, 5]
[3, 4, 5, 6]
[4, 6, 8, 10]

今回は引数で指定した数の配列を作ってみました。今回引数は(2, 5)となりますので何もブロックが渡されない15行目では「block_given?」は「false」となるため「else~end」の処理が実行され、配列を追加して[2, 3, 4, 5]という配列が出来上がります。

16、17行目ではブロックが渡されているので「if~else」の処理が実行されます。ここで「yield」が実行されてブロック内で記述した処理が行われて配列が作られることになります。

ブロック内のbreakとnext

上記のArray_Create.rbに記載した「test_yield」メソッドを呼び出す際のブロックにbreakやnextを使うとどうなるかを見て行きましょう。

気になる実行結果は↓の通りです。

nil
[1, 2, 3, nil, nil]

breakをブロック内でつかった場合は「test_yield」メソッドで作られる予定だった配列自体の作成が完全に飛ばされて「nil」が返ってきます。「next」の場合はブロック内の処理のみ飛ばされるだけで配列自体の作成はできました。

nextの挙動

nextの挙動がいまいち分からなかったので下記のように実行してみました。

[1, 2, nil, 4, 5]

今回は「next if num == 3」で3回目のみ「next」を実行するように記述しました。この実行結果からブロック内の「next」も通常の場合と同様に指定した回のみ「nil」を返してくることがわかりました。

最後に

こんなに脈絡のない文章をここまで読んで頂きありがとうございます。最後に今回記事を作成するにあたって参考させて頂いたページを載せておきます。

参考:Ruby の yield って結局なんなの? 参考:メソッド呼び出し(super・ブロック付き・yield)

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

ABOUTこの記事をかいた人

気になること&知らないことをひたすらググることを得意としています。このブログでは最近気になったことを中心に記事を書いていきます。記事には間違った情報を書かないように細心の注意を払っていますが、もし誤りがあった場合はコメントやお問い合わせフォームからご指摘いただければ幸いです。