Rubyのブロック、ブロック引数、Proc、yieldっていうJavaには無い機能についてだいたい分かったので、忘備録としてまとめました。
スポンサーリンク
ブロックとは
Rubyではdo ~ endの一塊(処理の塊)をブロックと言う。
do ~ endの代わりに{ ~ }を使うことも可能。
ブロックは、単独では存在できない。
1 2 3 | do puts "Hello" end |
↑いきなりこんなことするとエラー。
ブロックは、一連の処理を関数の引数に渡す為に使う。以下の2文は全く同じで、each関数の引数としてブロックを渡している。
1 2 3 4 5 6 7 8 | [1, 2, 3].each do |i| puts i*2 end #出力結果 2 4 6 |
1 2 3 4 5 6 7 8 | [1, 2, 3].each {|i| puts i*2 } #出力結果 2 4 6 |
ブロックの中で| |を書くことでその処理に使う値を受け取っている。
値を受け取らない場合は、| |は書かなくてもよい。この場合、単に配列の要素の数だけdo処理が繰り返されている。
1 2 3 4 5 6 7 8 | [1, 2, 3].each do puts "Hello" end #出力結果↓ Hello Hello Hello |
スポンサーリンク
ブロック引数とは
上記のように、処理ブロックを丸ごと受け取る引数をブロック引数と言う。
ブロック引数は普通の引数のように関数の( )の中に入れてはいけない。普通の引数のように「,」で区切って渡すのではなく、他の引数の最後にスペースを空けて付け足す。{ }ブロックを渡す場合はスペースは不要。
ブロック引数を受け取る関数を定義するには&blockという仮引数を書く。ただしブロック引数は1つしか受け取れない。引数の最後の1つとして書かなければならない。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | def receive_block(&block) puts "受け取ったブロックを実行します" block.call puts "実行しました" end receive_block do puts "Hello block" end #出力結果 受け取ったブロックを実行します Hello block 実行しました |
block.callで受け取ったブロックを実行する。
block.callは引数を受け取ることができ、その受け取った引数はブロックに渡される。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | def receive_block(str, &block) puts "受け取ったブロックを実行します" block.call(str) puts "実行しました" end receive_block "Hello block" do |message| puts message end #出力結果 受け取ったブロックを実行します Hello block 実行しました |
2つの引数 + ブロック引数を受け取る例↓
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | def receive_block(i, j, &block) puts "受け取ったブロックを実行します" block.call(i, j) puts "実行しました" end receive_block 2, 3 do |i, j| puts "#{i} + #{j} = #{i+j}" end #出力結果 受け取ったブロックを実行します 2 + 3 = 5 実行しました |
Procとは
Procとは、処理の塊をオブジェクト化したもの。処理の塊であるブロックをProcクラスのコンストラクタに渡すことで、Procインスタンスを作ることができる。
1 2 3 | proc = Proc.new do puts "Hello proc" end |
インスタンス化したProc(処理の塊オブジェクト)は、call関数で実行することが出来る。
1 2 3 4 5 6 7 8 | proc = Proc.new do puts "Hello proc" end proc.call #実行結果 Hello proc |
引数を受け取る処理の塊も作れる↓
1 2 3 4 5 6 7 8 | proc = Proc.new do |message| puts message end proc.call("Hello proc") #実行結果 Hello proc |
上述のブロック引数を受け取る宣言として使う&blockと、関数定義部分のblockは、ブロックとProcインスタンスの関係であり、内部的に受け取ったブロックをProcインスタンス化している。
&はブロックを表す記号であり&blockのblockはブロック引数につけた仮引数名にあたるので実は何でも良い。
つまり引数として受け取ったblockという名のブロックを、blockという名のProcインスタンスに変換しているからblock.callで実行することができる。
ブロック引数名をbにした場合↓
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | def receive_block(&b) puts "受け取ったブロックを実行します" b.call puts "実行しました" end receive_block do puts "Hello block" end #出力結果 受け取ったブロックを実行します Hello block 実行しました |
ブロック引数受け取るのではなく、Procインスタンスを引数として受け取る書き方もできる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | def receive_proc(proc) puts "受け取ったProcインスタンスを実行します" proc.call puts "実行しました" end proc = Proc.new do puts "Hello proc" end receive_proc proc #実行結果 受け取ったProcインスタンスを実行します Hello proc 実行しました |
yieldとは
しかしながら、そもそもブロック引数を1つしか受け取れないんだったら、ブロック引数に名前を付ける意味なくね?いちいちblock.callなんて無駄にインスタンス名書かせんなよ。ってことで、Procインスタンスの実行部分(block.call)はyieldで置き換えられる。
1 2 3 4 5 6 7 8 9 | def receive_block(&block) puts "受け取ったブロックを実行します" yield puts "実行しました" end receive_block do puts "Hello block" end |
引数も同じように受け取れる↓
1 2 3 4 5 6 7 8 9 | def receive_block(str, &block) puts "受け取ったブロックを実行します" yield(str) puts "実行しました" end receive_block "Hello block" do |message| puts message end |
関数定義部分にyieldがあるってことは、ブロック引数を受け取ることを意味するので、そもそも&blockも書かなくてよくね?ってことで、yieldがあれば&blockは省略可能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | def receive_block(str) puts "受け取ったブロックを実行します" yield(str) puts "実行しました" end receive_block "Hello block" do |message| puts message end #実行結果 受け取ったブロックを実行します Hello block 実行しました |
ただし、&blockを省略した場合、定義部分のyieldの有無がブロック引数を受け取るかどうかを決めることになるので、可読性が大幅に落ちる。注意されたし。
[…] Ruby のブロック、ブロック引数、Proc、yield をまとめてみた | Java からの Ruby on Rails 入門 […]
by block / Proc / lambda [Ruby] – Site-Builder.wiki 2019年1月3日 5:23 AM