プログラミング言語III・講義ノート
岡山県立大学情報工学部情報通信工学科・國島丈生
Rubyでは、ブロックを引数に取るメソッドを定義することができる。用途は主に次の3通りがある。
特に1, 2の用途のメソッドをイテレータ(Iterator)という。
3.times { |i| puts i }
この例のメソッド times がイテレータであり、後ろの { |i| puts i } という部分(ブロック)を引数として呼び出されている。ブロック内の | | で囲まれた変数は、ブロックへの引数と考えて良い。
times の実装はだいたい次のようになっている。
def times
1から(メソッドの対象となる整数)[1]までの整数n について
yield n
end
yieldは、引数として与えられたブロックを実行する。上の例では、そのとき、ブロックに引数nを渡す。上のコードからわかるように、この例では、整数1, 2, 3が順にブロックの引数iに渡り、実行される。結果として、以下のような出力となる。
1
2
3
代表的なイテレータを以下に示す。
繰り返し実行:times, upto, downto
3.times { |i| puts i }
1.upto(4) { |i| puts i } # 1から4まで順に実行
5.downto(2) { |i| puts i } # 5から2まで順に実行
n.upto(m) の場合は n < m、n.downto(m)の場合は n > m でなければならない。
2つの値m, n(m < n)[2]に対して、m .. n は「m以上n以下の値すべての並び」を表す。こういう値のことを、Rubyでは「範囲」と呼ぶ[3]。範囲に対するイテレータの代表格は each である。
(2 .. 5).each { |i| puts i } # 2, 3, 4, 5 のすべてについてブロックを実行
(“a” .. “c”).each { |i| puts i } # “a”, “b”, “c” のすべてについてブロックを実行
代表格は、要素それぞれについて順にブロックを実行する each(配列、ハッシュに対して)、each_byte(文字列に対して)である。
[1, 2, 3].each { |i| puts i }
“abcde”.each_byte { |i| puts i }
Ruby1.8では、文字列は文字(ASCIIコード)の配列とみなされるため、出力は次のとおり。
97
98
99
100
101
{ “Jan” => 1, “Feb” => 2, “Mar” => 3}.each { |key, value| print key, “:”, value, “¥n” }
ハッシュのイテレータeachでは、ハッシュのキーが第1引数、値が第2引数として渡される。したがって、出力は次のとおり。
Jan:1
Feb:2
Mar:3
配列には、添字もブロックに渡すイテレータ each_with_index もある。要素の値が第1引数、添字が第2引数となる。
[“a”, “b”, “c”].each_with_index { |v, i| print i, “:”, v, “¥n” }
出力は次のとおり。
0:a
1:b
2:c
高階関数(Higher Order Function)[4]に相当するイテレータを次にいくつか示す。
b = [“10”, “9”, “8”]
b.sort #=> [“10”, “8”, “9”], 文字列なので辞書順でソートされる
b.sort { |x, y| x.to_i <=> y.to_i } #=> [“8”, “9”, “10”], 要素の大小比較の方法をブロックとして渡している。x.to_i (xを整数に変換した値) と y.to_i (yを整数に変換した値)を大小比較している。
[1, 2, 3].map { |x| x * x } #=> [1, 4, 9], 各要素にブロックを適用した配列を返す
[1, 2, 3].select { |x| x % 2 == 1 } #=> [1, 3], ブロックが真となる要素のみからなる配列を返す
[1, 2, 3].inject(0) { |s, i| s + i } #=> 6, ((0 + 1) + 2) + 3 を計算する。sにはそれまでの計算結果が、iには各要素が渡される。injectの引数として初期値を与える。
ブロック付きメソッドは、ほかにファイルなどのリソース管理に用いられる。以下のコードでは、File.openメソッドの結果がブロックの引数として渡され、ブロックが実行される。ファイルのクローズ処理が、ブロック終了時に自動的に行われるため、プログラマが記述する必要がない。
File.open(“sample.txt”) { |f|
while line = f.gets do
print line
end
}
[1]この例の場合は3になる。
[2]n >= m でも範囲にはなるが、イテレータが使えない。
[3].. を範囲演算子という。
[4]2年次開講科目「プログラミング言語II」を参照。