Proc.new
では手続きオブジェクトが作られませんがブロックや手続きオブジェクトを活用するメソッドをブロック付きメソッド呼び出しと呼びます。
制御構造(特にループ)の抽象化のために用いられたため、イテレータと呼ばれることもありますが、 単なるブロックの呼び出しなどiterate(繰り返し)を行わないような場合でも活用できます。
下記はイテレータとして作成されたメソッドを使用したケースです。
data = [1, 2, 3] data.each do |i| print i, "\n" end
このコードの出力はこのようになります。
$ ruby test.rb 1 2 3
つまりdo
とend
で囲まれたブロックがメソッドに渡され、メソッド内で使用されます。
メソッド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
ブロックよりも結合強度が強いためです。
ブロック付きメソッドにブロックを渡すには、メソッドの後ろにブロックを置く 方法の他に、手続きオブジェクト(を指す変数、定数)の前に&を つけて引数として渡す方法があります。
メソッドの中からブロックを使用するには、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"}
Proc.new
では手続きオブジェクトが作られませんがProc.new
は、ブロックを与えられないと手続きオブジェクトを生成できず、
エラーになります。メソッド定義の中で
使われるブロックなしのProc.new
は、メソッド呼び出しにブロックが与えられて
いることを仮定しています。