Ruby 1.9.2 リファレンスマニュアル > spec/block
ブロックとブロック付きメソッド呼び出し
ブロック付きメソッド呼び出し
例:
[1,2,3].each do |i| print i*2, "\n" end [1,2,3].each {|i| print i*2, "\n" }
文法:
method(arg1, arg2, ...) do [`|' 式 ... `|'] 式 ... end method(arg1, arg2, ...) `{' [`|' 式 ... `|'] 式 ... `}' method(arg1, arg2, ..., `&' proc_object)
ブロック付きメソッドとは制御構造の抽象化のために用いられる メソッドです。最初はループの抽象化のために用いられていたため、 特にイテレータと呼ばれることもあります。 do ... end または { ... } で囲まれたコードの断片 (ブロックと呼ばれる)を後ろに付けてメソッドを呼び出すと、その メソッドの内部からブロックを評価できます。 ブロック付きメソッドを自分で定義するには yield 式を使います。
{ ... } の方が do ... end ブロックよりも強く結合します 次に例を挙げますが、このような違いが影響するコードは読み辛いので避けましょう:
foobar a, b do .. end # foobarの引数はa, bの値とブロック foobar a, b { .. } # ブロックはメソッドbの引数、aの値とbの返り値とがfoobarの引数
ブロックの中で初めて代入された(宣言された)ローカル変数はその ブロックの中でだけ有効です。例えば:
foobar { i = 20 # ローカル変数 `i' が宣言された ... } print defined? i # `i' はここでは未定義なので false foobar a, b do i = 11 # まったく別の変数 i の宣言 ... end
以下は逆にブロック外でも有効な例です。
i = 10 [1,2,3].each do |m| p i * m # いきなり i を使える end
ブロックの部分だけを先に定義して変数に保存しておき、後からブロック付きメソッドに渡すことも出来ます。 それを実現するのが手続きオブジェクト(Proc)です。 それをブロックとして渡すにはブロック付きメソッドの最後の引数として `&' で修飾した手続きオブジェクトを渡 します。Proc の代わりにメソッドオブジェクト(Method)を渡す ことも出来ます。この場合、そのメソッドを呼ぶ手続きオブジェクトが生成さ れ渡されます。
# 1引数の手続き(その働きは引数をpで印字すること)を生成し、変数pobjに格納 pobj = proc {|v| p v } [1,2,3].each(&pobj) # 手続きオブジェクトをブロックの代わりに渡している => 1 2 3
to_proc メソッドを持つオブジェクトならば、`&' 修飾した引数として渡すことができます。デフォルトで Proc、Method オブジェ クトは共に to_proc メソッドを持ちます。to_proc はメソッド呼び出し時に実 行され、Proc オブジェクトを返すことが期待されます。
class Foo def to_proc Proc.new {|v| p v} end end [1,2,3].each(&Foo.new) => 1 2 3
ブロック付きメソッドの戻り値は、通常のメソッドと同様ですが、ブロックの中から 制御構造/break により中断された場合は nil を返します。
break に引数を指定した場合はその値がブロック付きメソッドの戻り値になります。
yield
自分で定義したブロック付きメソッドでブロックを呼び出すときに使います。 yield に渡された値はブロック記法において | と | の間にはさまれた 変数(ブロックの引数)に代入されます。
例:
yield data
文法:
yield `(' [式 [`,' 式 ... ]] `)' yield [式 [`,' 式 ... ]]
引数をブロックの引数として渡してブロックを評価します。yield は イテレータを定義するために クラスとメソッドの定義/メソッド定義 内で使用します。
# ブロック付きメソッドの定義、 # その働きは与えられたブロック(手続き)に引数1, 2を渡して実行すること def foo yield(1,2) end # fooに「2引数手続き、その働きは引数を配列に括ってpで印字する」というものを渡して実行させる foo {|a,b| p [a, b] } # => [1, 2] (要するに p [1, 2] を実行した) # 今度は「2引数手続き、その働きは足し算をしてpで印字する」というものを渡して実行させる foo {|a, b| p a + b } # => 3 (要するに p 1 + 2 を実行した) # 今度のブロック付きメソッドの働きは、 # 与えられたブロックに引数10を渡して起動し、続けざまに引数20を渡して起動し、 # さらに引数30を渡して起動すること def bar yield 10 yield 20 yield 30 end # barに「1引数手続き、その働きは引数に3を足してpで印字する」というものを渡して実行させる bar {|v| p v + 3 } # => 13 # 23 # 33 (同じブロックが3つのyieldで3回起動された。 # 具体的には p 10 + 3; p 20 + 3; p 30 + 3 を実行した) # Array#eachの(粗製乱造の)類似品 def iich(arr) # 引数に配列を取る idx = 0 while idx < arr.size yield(arr[idx]) # 引数の各要素毎に、その要素を引数にしてブロックを起動 idx += 1 end end sum = 0 iich([1, 4, 9, 16, 25]) {|elem| sum += elem } p sum # => 55
ブロック引数の代入は演算子式/多重代入と同じルールで行われます。 また yield を実行したメソッドにブロックが渡されていない (ブロック付きメソッド呼び出しではない)時は例外 LocalJumpError が発生します。
yield はブロック内で最後に評価した式の値を返します。また、 制御構造/next によりブロックの実行が中断された場合は nil を返します。
next に引数を指定した場合はその値が yield の戻り値になります。
ブロックパラメータの挙動
メソッド呼び出しと挙動が異なります。 lambda でないブロックを呼び出したとき
- 引数の数が違ってもエラーになりません。
- 配列をひとつ渡したときにそれが引数の並びとして展開されることがあります。
def foo yield 1,2,3 end foo{|v| p v} #=> 1 def bar yield [1,2,3] end bar{|a, b, c| p a} #=> 1 def hoge yield [1,2,3],4,5 end hoge{|a, b, c| p a} #=> [1,2,3]