イテレータ
イテレータ(英語: Iterator)とは、プログラミング言語において配列やそれに類似するデータ構造の各要素に対する繰返し処理の抽象化である。実際のプログラミング言語では、オブジェクトまたは文法などとして現れる。反復するためのものの意味で反復子(はんぷくし)と訳される。繰返子(くりかえし)という妙訳もある。
イテレータには内部イテレータと外部イテレータの区分がある。
目次 |
種類 [編集]
内部イテレータ [編集]
内部イテレータとは、要素を格納したオブジェクトが、自身の要素に対して繰り返しながらユーザコードを逐一呼び出す方式である。繰り返しの記述が(オブジェクトの)内部にあるためこの名がある。一般にForeach文などとして言語で用意されている。
利点 [編集]
- 繰り返しの記述がメソッド内部に隠蔽されているため、記述が簡潔になる
- プログラマは繰り返しを気にせず、実際に要素に対してしたいことに注力できる
外部イテレータ [編集]
外部イテレータとは、要素を参照するオブジェクトを定義し、それを典型的には一つずつ先の要素を指すよう変化させながら要素をなぞる方式である。繰り返しの記述が(オブジェクトの)外部にあるためこの名がある。
利点 [編集]
- 繰り返しを途中で止め、途中結果を保存してあとで再開することができる
- 複数のコンテナを平行して繰り返すことができる
- 特別な文法の支援なく実装できる
各言語における例 [編集]
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ではメソッド__iter__()とnext()を定義したクラスをイテレータとして扱う。__iter__()はイテレータ自身を返し、next()は次の要素を返す。
イテレータと関係付けられたコンテナクラスにも__iter__()メソッドを定義すれば、そのコンテナは反復可能になる。反復可能なオブジェクトは組み込みのリストのようにfor文(Foreach文)に渡して内部イテレータとして使うことができ、そのように使用するのが一般的である。コンテナのイテレータを取得するには、iter()組み込み関数を使う。
cont = iteratable_container() # 外部イテレータとして it = iter(cont) while 1: try: print it.next() except StopIteration: # 要素が残っていないならば、 # next()はStopIteration例外を発生させる break # 内部イテレータとして for element in cont: print element
また、Pythonではジェネレータ関数によって内部イテレータ風に記述することもできる。ジェネレータは関数内で要素を巡回し(あるいは、しばしばその都度作成し)、yield文でその時点での要素を返す。
def fruit_generator(): yield 'banana' yield 'apple' yield 'orange' for n in fruit_generator(): print n
Ruby [編集]
Rubyでは、ブロック付きメソッドを内部イテレータとして利用する。メソッドに do ~ end または { ~ } 形式の「ブロック」を与えると、メソッド内部での yield の呼び出しでそのブロックへ制御が渡る(コールバック)。メソッドが要素を繰り返しながら yield すれば、それはイテレータとなる。
ary.each do |i| p i end
.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