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]

http://cvs.m17n.org/~akr/diary/2007-08.html#a2007_08_16_1

Libraries

Classes