イテレータ
イテレータ(英語: Iterator)とは、プログラミング言語において配列やそれに類似するデータ構造の各要素に対する繰返し処理の抽象化である。実際のプログラミング言語では、オブジェクトまたは文法などとして現れる。反復するためのものの意味で反復子(はんぷくし)と訳される。繰返子(くりかえし)という妙訳もある。
各言語における例
C++
C++では、STLが外部イテレータの枠組みを定義している。この枠組みはポインタと互換性を持つよう定められているため、ポインタをイテレータとして利用することができる。
template<typename InputIterator>
void printall(InputIterator first, InputIterator last)
{
for(; first != last; ++first)
{
std::cout << *first << std::endl;
}
}
なお、イテレータの種類によって用いることの出来る演算子に違いがある。例えば、vectorコンテナのイテレータとlistコンテナのそれは共にインクリメントを用いて次の要素を指すことが出来るが、足し算は前者でのみ定義されている。これはlistコンテナの要素が、直前の要素へのポインタを保持していないためである。
Delphi
Delphiでは、バージョン2005よりfor-in
構文によるイテレータがある。ユーザによるイテレータはMoveNext
メソッドやCurrent
プロパティを任意のクラス等に実装することで定義でき、型に厳格なPascal系言語ながらこれらを実装するだけでfor-in
により認識されるというダックタイピングにも似た仕組みとなっている。
for item in items do
Writeln(item);
Java
Javaでは、Iteratorインタフェイス族を実装するオブジェクトが外部イテレータとなる。
Iterator it = list.iterator();
while (it.hasNext())
{
Object val = it.next();
System.out.println(val.toString());
}
Perl
Perlには、foreach
、each
といった繰り返しのキーワードがある。
他に、Tie機能(変数操作のオーバーロード)でユーザーデータに対するイテレータを定義できる。
# foreachを使った例。配列・リストに対する反復
foreach my $element(@array){
print $element, "\n";
}
# eachを使った例。ハッシュ(連想配列)に対する反復
while(my($key, $value) = each %hash){
print "$key=$value\n";
}
PHP
PHPではIteratorインタフェイスを実装することにより、任意のイテレータを定義することができ、foreach
、while
といったキーワードでイテレータを簡単に利用することができる。
また、大抵のオブジェクトにはあらかじめイテレータが実装されている。
# foreachを使った例。配列・連想配列・オブジェクト等に対し全く同じように使用できる。
foreach ( $elements as $key=>$value ){
print $key . "=" . $value . " \n";
}
Python
Pythonは次の要素を返す__next__()
メソッドを持つオブジェクトを外部イテレータとして使う。コンテナオブジェクトの__iter__()
メソッドがイテレータを返す。(便宜上、イテレータの__iter__()
は自身を返す)
通常のプログラミングでは、obj.__iter__()
のように直接呼ぶのではなく、組込み関数iterを使ってiter(obj)
のようにする。同様に、通常の用法で呼ぶことを前提とした場合は__next__()
ではなくnext()
を使う。for文(Foreach文)はイテレータが使える場合はイテレータを使うが、そうでないコンテナオブジェクトに対しては直接、__getitem__()
メソッドにより要素を取得し繰返しを実行する。
Pythonはfor文を使うから内部イテレータである、と考える者がいるようだが誤解である。
cont = iteratable_container()
# イテレータを直接使う
it = iter(cont)
while 1:
try:
print it.next()
except StopIteration:
# 要素が残っていないならば、
# next()はStopIteration例外を発生させる
break
# for文で使う
for element in cont:
print element
また、Pythonには一種のコルーチンを記述できるジェネレータもある。ジェネレータはイテレータを返す関数で、yield文により、__next__()
で実行される手続きを次々と記述できる。
def fruit_generator():
yield 'banana' # 最初の __next__() によりここまで実行され 'banana' を返す
yield 'apple' # 次の __next__() によりここまで実行され 'apple' を返す
yield 'orange' # 3回目の __next__() によりここまで実行され 'orange' を返す
for fruit in fruit_generator():
print(fruit)
it = fruit_generator()
print(next(it))
print(next(it))
print(next(it))
print(next(it)) # この行で StopIteration 例外になる
Ruby
Rubyでは、Arrayなどのコンテナオブジェクトが、eachなどのイテレートするメソッドを持っている内部イテレータである。メソッド呼出しの直後に { ~ }
という書式で「ブロック」を書くと、その中の手続きが繰返し実行される。「ブロック付きメソッド」(あるいは、「ブロック付きメソッド呼び出し」)と言い、これをイテレータとも呼ぶ。
ブロックを付けて呼ばれたメソッド中でyieldを実行すると、ブロックの中の手続きが実行される。Pythonのジェネレータと同じyieldというキーワードで、コードの見た目も似たものになるが、具体的な働きは全く異なることに注意。
class MyObj
def my123
yield 1
yield 2
yield 3
end
end
arr = ["a", "b", "c"]
arr.each {|x|
p x
}
obj = MyObj.new
obj.my123 {|x|
p x
}
.NET Framework
イテレータは、.NET Frameworkでは列挙子と呼ばれ、IEnumerator
インターフェイスによって表している。IEnumerator
インターフェイスはMoveNext()
メソッドを定義しており、このメソッドを使用することにより次の要素に進むと同時に、コレクションの末尾に到達するかどうかを判定する。Current
プロパティを使用することによってコレクション内部の要素を取得する。コレクションの最初の要素に戻す方法としてReset()
メソッドを使用する。
列挙子を得るにはIEnumerable
インターフェイスに定義しているGetEnumerator()
メソッドを使用する。一般的にコレクション クラスはこのインターフェイスを実装する。GetEnumerator()
を呼び出さず、foreach
を代わりに使用することもできる。両方のインターフェイスは、.NET 2.0でジェネリック (System.Collections.Generic
) として拡張された。
C# 2.0
// 明示的な使い方
IEnumerator<MyType> iter = list.GetEnumerator();
while (iter.MoveNext())
Console.WriteLine(iter.Current);
// 暗黙的な使い方
foreach (MyType value in list)
Console.WriteLine(value);
C# 2.0は生成プログラムをサポートする。IEnumerator
またはIEnumerable
を返すメソッド内でのみyield return
ステートメントを使用できる。コンパイラが適切なインターフェイスを実装する新しいクラスに変えてオブジェクトを返している。
Visual Basic
' 明示的な使い方
Dim iter As IEnumerator(Of MyType) = list.GetEnumerator()
Do While iter.MoveNext()
Console.WriteLine(iter.Current)
Loop
' 暗黙的な使い方
For Each value As MyType In list
Console.WriteLine(value)
Next value