ブロックや手続きオブジェクトを活用するメソッドをイテレータと呼びます.
イテレータは制御構造(特にループ)の抽象化のために用いられるメソッド の一種です.
といってもよくわかりませんね.実際の例を見た方が早いかもしれません.イ テレータはコレクションの各要素に対して同じ処理を繰り返したいような場合 によく使われます.例えばこんな感じです.
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種類の方法で行うことができます.
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
は,メソッド呼出しにブロックが与えられて
いることを仮定しています.