Published using Google Docs
プログラミング言語III講義ノート/イテレータ

プログラミング言語III・講義ノート

岡山県立大学情報工学部情報通信工学科・國島丈生

イテレータ

どんなものか

Rubyでは、ブロックを引数に取るメソッドを定義することができる。用途は主に次の3通りがある。

  1. 回数の決まっている繰り返し構文の置き換え
  2. 高階関数
  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」を参照。