Proc.new
では手続きオブジェクトが作られませんがブロックや手続きオブジェクトを活用するメソッドをイテレータと呼びます。
イテレータは制御構造(特にループ)の抽象化のために用いられるメソッド の一種です。
といってもよくわかりませんね。実際の例を見た方が早いかもしれません。イ テレータはコレクションの各要素に対して同じ処理を繰り返したいような場合 によく使われます。例えばこんな感じです。
data = [1, 2, 3] data.each do |i| print i, "\n" end
このコードの出力はこのようになります。
$ ruby test.rb 1 2 3
つまりdo
とend
で囲まれたブロックが配列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
は、メソッド呼び出しにブロックが与えられて
いることを仮定しています。