3. ブロック付きメソッド呼び出し

3.1 ブロック付きメソッド呼び出しとは何ですか

ブロックや手続きオブジェクトを活用するメソッドをブロック付きメソッド呼び出しと呼びます。

制御構造(特にループ)の抽象化のために用いられたため、イテレータと呼ばれることもありますが、 単なるブロックの呼び出しなどiterate(繰り返し)を行わないような場合でも活用できます。

下記はイテレータとして作成されたメソッドを使用したケースです。

data = [1, 2, 3]
data.each do |i|
  print i, "\n"
end

このコードの出力はこのようになります。

$ ruby test.rb
1
2
3

つまりdoendで囲まれたブロックがメソッドに渡され、メソッド内で使用されます。 メソッドeachはブロックをdataの各要素に対して繰り返し適用します。

これをCで書くと次のようになります。

int data[3] = {1, 2, 3};
int i;
for (i = 0; i < 3; i++) {
  printf("%d\n", data[i]);
}

forを使って書いた場合は繰り返し処理を自分で処理しなくてはなりませんが、 ブロック付きメソッド呼び出しの場合はメソッド側で処理するため、境界条件の判定ミスで バグが生まれる可能性が減少します。

また、do...endの代わりに{...}を使うこともできます。

data = [1, 2, 3]
data.each { |i|
  print i, "\n"
}

このコードは先の例と全く同じ動作をします。ただし、do...end{...}で動作が異なる場合があります。

foobar a, b do .. end # foobar がブロック付きメソッドとして呼び出されます
foobar a, b { .. }    # b がブロック付きメソッドとして呼び出されます

これは{ }の方がdoブロックよりも結合強度が強いためです。

3.2 ブロック付きメソッドにブロックを渡すにはどうすればいいですか

ブロック付きメソッドにブロックを渡すには、メソッドの後ろにブロックを置く 方法の他に、手続きオブジェクト(を指す変数、定数)の前に&を つけて引数として渡す方法があります。

3.3 ブロックは呼び出したメソッドの中からどのように使われますか

メソッドの中からブロックを使用するには、yield制御構造、ブロック引数、 Proc.newの3種類の方法で行うことができます。( Cで書かれた拡張ライブラリ の中では、rb_yieldが使われます。)

yieldの場合には、yieldの後ろに続く引数が、ブロック パラメータとしてブロックに渡され、ブロックが実行されます。

ブロック引数は、メソッド定義の引数の最後に&methodという形で 置かれ、メソッドの中で、method.call(args...)という形で呼ばれます。

Proc.newは、メソッドの中で使われたときには、引数としてそのメソッドに 渡されたブロックをとり、そのブロックを内容とする手続きオブジェクトを 生成します。procまたはlamdaも同様です。

def a (&b)
  yield
  b.call
  Proc.new.call
  proc.call
  lambda.call
end
a{print "test\n"}

3.4 Proc.newでは手続きオブジェクトが作られませんが

Proc.newは、ブロックを与えられないと手続きオブジェクトを生成できず、 エラーになります。メソッド定義の中で 使われるブロックなしのProc.newは、メソッド呼び出しにブロックが与えられて いることを仮定しています。



rubyist ML