メソッドの定義でyieldを記述するとメソッドをブロック付きで実行した際にそのブロックをyieldの部分で実行されます。yieldの部分は定義したメソッドの外側で定義(呼び出しの際のブロックで表現)するのですこしややこしくなりますが、取り敢えずまずは下記のメソッドをみて挙動を確認して行きましょう。
1 2 3 4 5 6 7 8 9 10 11 12 |
def test_yield while true yield end end num = 1 test_yield do puts num num += 1 break if num > 3 end |
1 2 3
上記では「test_yield」メソッドを実行するとまず、2行目の「while true」にて無限ループに入ります。ですので「test_yield」メソッドを呼び出すときに「break」などで無限ループを抜ける処理を記述する必要があります。
Contents
処理の流れを確認
それでは上記のメソッドで処理の流れを見て行きましょう。
- 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が実行されメソッドの処理が終了。
もっと簡単なスクリプトをみてみる
処理内容を書いていってたら、これでは微妙だと思ったのでもっとシンプルなスクリプトを書いてみました。
1 2 3 4 5 6 7 8 9 |
def test_yield yield end test_yield do puts "yield" end test_yield { puts "yield!!!" } |
yield yield!!!
今回は繰り返しを省いたのでかなり見やすくなったかと思います。回りくどい書き方になりましたが、これで「test_yield」が呼ばれた際にブロックでの記述が渡されていることがわかります。
ブロック変数をつけてみる
次にブロック変数をメソッド呼び出しの際に記述して、処理を行う方法を見て行きましょう。ブロック変数は今回で言うと「|a|」の部分になります。
1 2 3 4 5 6 7 8 |
def test_yield num = 1 yield(num) end test_yield do |a| puts a, "yield" end |
1 yield
test_yieldメソッド内で「num = 1」を定義し、yieldに引数として渡しています。yieldではブロックの処理を行う際にブロック変数にこの「num」の値が使えます。今回はブロック変数を「a」として「num」の値「1」が「a」に代入され、ブロック内でputsされることになります。
また、上記と同じ要領でブロック変数を増やしていくこともできます。
1 2 3 4 5 6 7 8 9 |
def test_yield num = 1 str = "yieldって難しいね。" yield(num, str) end test_yield do |a, b| puts a, b end |
1 yieldって難しいね。
メソッド自体に引数を渡してみる
上記のスクリプトを少し変更して「test_yield」メソッド自体に引数を渡して、それを更に「yield」に渡し、ブロック内でその引数の処理を決める方法を見ていきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
def test_yield(num, str) yield(num, str) end num = 2 str = "yieldって難しいね。" test_yield(num, str) do |a, b| puts a, b end test_yield(num, str) do |a, b| puts "↓#{a}回puts↓" a.times do puts b end end |
2 yieldって難しいね。 ↓2回puts↓ yieldって難しいね。 yieldって難しいね。
8行目のtest_yieldメソッドの呼び出しはただ単にputsするだけのブロックを渡しましたが、12~17行目は少し複雑な感じになっています。
12行目で渡された引数は結局ブロック変数「a,b」に代入されて処理が行われています。ブロック内の記述の方がメソッドよりも比重が多くなってしまいました。もうもはやどちらがメソッドなのかわかりませんね。笑
1番初めのスクリプトと同様の処理をしてみる
結構まとまりがなくて申し訳ありませんが、再度1番初めに紹介したスクリプト「yield.rb」と似たような処理をブロック変数を用いて記述してみます。そして今回は処理のほとんどをメソッド側で定義してみました。
1 2 3 4 5 6 7 8 9 10 11 |
def test_yield num = 1 while num <= 3 yield(num) num += 1 end end test_yield do |block_num| puts block_num end |
1 2 3
これでメソッド内でループ処理のほとんどが実行されるようになり、ブロックではブロック変数を用いての処理のみ記述することができるようになりました。今回はブロックの処理は3回行われて、|block_num|のブロック変数が「1、2、3」の三回変化することになります。
yieldの戻り値
いろいろと挙動を調べていくうちにyieldをメソッド内で実行するとその戻り値を取得できるということに気が付きました。また、yieldの戻り値はブロック内の記述の1番最後の値となるようです。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
def test_yield num = 1 ary = [] while num <= 3 a = yield(num) ary.push(a) num += 1 end return ary end p test_yield { |block_num| block_num } p test_yield { |block_num| block_num * 2 } |
[1, 2, 3] [2, 4, 6]
今回は3行目で変数「ary」に配列オブジェクトを新しく定義しました(Array.newと同じ意味)。そして5行目で実行した「yield」の結果を変数「a」に代入し、6行目で「push」メソッドを使い、要素を追加していっています。
block_given?メソッド
「block_given?」メソッドは記述したメソッドが呼び出された際にブロックが与えられていれば「true」与えられていなければ「false」を返します。
1 2 3 4 5 |
def test_block block_given? end p test_block {} p test_block |
true false
4行目ではブロックを渡したので「true」が返ってきて5行目ではなにも渡していないので「false」となります。これを利用してブロックを渡したときと渡さないときで挙動が違うメソッドを作ってみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
def test_yield(strat_num, end_num) ary = Array.new while strat_num <= end_num if block_given? a = yield(strat_num) ary.push(a) else ary.push(strat_num) end strat_num += 1 end return ary end p test_yield(2, 5) p test_yield(2, 5) { |a| a + 1 } p test_yield(2, 5) { |a| a * 2 } |
[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を使うとどうなるかを見て行きましょう。
1 2 3 4 5 6 7 8 9 10 11 12 |
# test_yieldは省略しました。詳しくは上記「Array_Create.rb」を参照 a = test_yield(1, 5) do |num| break if num > 3 num end p a b = test_yield(1, 5) do |num| next if num > 3 num end p b |
気になる実行結果は↓の通りです。
nil [1, 2, 3, nil, nil]
breakをブロック内でつかった場合は「test_yield」メソッドで作られる予定だった配列自体の作成が完全に飛ばされて「nil」が返ってきます。「next」の場合はブロック内の処理のみ飛ばされるだけで配列自体の作成はできました。
nextの挙動
nextの挙動がいまいち分からなかったので下記のように実行してみました。
1 2 3 4 5 |
c = test_yield(1, 5) do |num| next if num == 3 num end p c |
[1, 2, nil, 4, 5]
今回は「next if num == 3」で3回目のみ「next」を実行するように記述しました。この実行結果からブロック内の「next」も通常の場合と同様に指定した回のみ「nil」を返してくることがわかりました。
最後に
こんなに脈絡のない文章をここまで読んで頂きありがとうございます。最後に今回記事を作成するにあたって参考させて頂いたページを載せておきます。
コメントを残す