イテレータ

出典: フリー百科事典『ウィキペディア(Wikipedia)』
ナビゲーションに移動 検索に移動

イテレータ英語: iterator)とは、プログラミング言語において配列やそれに類似する集合的データ構造(コレクションあるいはコンテナ)の各要素に対する繰り返し処理の抽象化である。実際のプログラミング言語では、オブジェクトまたは文法などとして現れる。JISでは反復子(はんぷくし)と翻訳されている[1][2]

ジェネレータ (プログラミング) の記事も参照のこと。

種類[編集]

内部イテレータ[編集]

hashなどのオブジェクトが高階関数として実装しているイテレータのことを内部イテレータという。 主にオブジェクト指向プログラミングの性質に基づき通常外部から明示的に使用することはないイテレータを指す[要出典]

各言語における例[編集]

C++[編集]

C++では、STLが外部イテレータの枠組みを定義している。この枠組みはポインタと構文上の互換性を持つよう定められているため、ポインタを用いるコードと同等のコードでイテレータを使用することができる。

template<typename InputIterator>
void printall(InputIterator first, InputIterator last)
{
    for(; first != last; ++first)
    {
        std::cout << *first << std::endl;
    }
}

C++ではイテレータの種類によって用いることのできる演算子に違いがある。例えば、ランダムアクセスができるstd::vectorのイテレータと、双方向イテレータであるstd::listのイテレータは共にインクリメント演算子及びデクリメント演算子を用いて前後の要素を指すことができる。しかし、任意の加算/減算はランダムアクセスイテレータでのみ定義されている。これは双方向イテレータが、前後の要素へのポインタしか保持していないためシーケンシャルアクセスしかできず、n 個先の要素への移動が定数時間で実行できないからである。

Delphi[編集]

Delphiでは、バージョン2005よりfor-in構文によるイテレータがある。ユーザーによるイテレータはMoveNextメソッドやCurrentプロパティを任意のクラス等に実装することで定義でき、型に厳格なPascal系言語ながらこれらを実装するだけでfor-inにより認識されるというダックタイピングにも似た仕組みとなっている。

for item in items do
  Writeln(item);

Java[編集]

Javaでは、インターフェースjava.util.Iteratorを実装するオブジェクトを外部イテレータとして扱うことができる。Java 1.5以降のIteratorジェネリクスに対応している。

import java.util.*;
// ...

final List<Object> list = new ArrayList<>();
list.add("hoge");
list.add(Integer.valueOf(100));
list.add(Double.valueOf(-0.5));

final Iterator<Object> it = list.iterator();
while (it.hasNext()) {
    final Object obj = it.next();
    System.out.println(obj.toString());
}

Java 1.5以降では、java.lang.Iterableインターフェイスを実装すると、拡張for文(foreach文)の対象にすることができる。

// java.util.List<E> は Iterable<E> を実装している。
for (final Object obj : list) {
    System.out.println(obj.toString());
}

Perl[編集]

Perlには、foreacheach といった繰り返しのキーワードがある。 他に、Tie機能 (変数操作のオーバーロード) でユーザーデータに対するイテレータを定義できる。

# foreachを使った例。配列・リストに対する反復
foreach my $element(@array){
    print $element, "\n";
}
# eachを使った例。ハッシュ(連想配列)に対する反復
while(my($key, $value) = each %hash){
    print "$key=$value\n";
}

PHP[編集]

PHPではIteratorインターフェイスを実装することにより、任意のイテレータを定義することができる。Iteratorインターフェースを実装したオブジェクトをforeachwhileで使用することで反復を行うことができる。 また、配列はIteratorインターフェースを暗黙に実装する。

# 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文においてはiterableを範囲にとって、暗黙的にiteratorを利用する点を指して内部イテレータと呼ばれる場合もある。

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ではEnumerableが、eachなどのイテレートするメソッドを持っている内部イテレータである。eachメソッド呼出しに { ... } という書式で「ブロック」を書くと、その中の手続きが繰返し実行される。

class MyObj
  def my123
    yield 1
    yield 2
    yield 3
  end
end

arr = ["a", "b", "c"]
arr.each do |x|
  p x
end

obj = MyObj.new
obj.my123 do |x|
  p x
end

.NET言語[編集]

C#VB.NETなどの.NET Frameworkに準拠する.NET言語において、反復子 (iterator) は値の順序付き列を産出 (yield) する文のブロック(狭義ではyield文を含むメソッド)を意味する[3][4][5]。これを反復子ブロック (iterator block) とも呼ぶ[6]。また、コレクションに対する列挙操作を行なう機能を提供するための媒介インターフェイス[7]を列挙子 (enumerator) と呼び、IEnumeratorインターフェイスによって表す。IEnumeratorインターフェイスはMoveNext()メソッドを定義しており、このメソッドを使用することによりコレクション中の次の要素に進むと同時に、コレクションの末尾に到達するかどうかを判定する。Currentプロパティを使用することによってコレクション内部の要素を取得する。イテレータを最初の要素に戻す方法としてReset()メソッドが定義されるが、常に使用可能であるとは限らない。

列挙子を得るには通例IEnumerableインターフェイスを実装するオブジェクトのGetEnumerator()メソッドを呼び出す。一般的にコレクション クラスはこのIEnumerableインターフェイスを実装する。GetEnumerator()を明示的に呼び出さず、foreach文を代わりに使用することもできる(GetEnumerator()はコンパイラによって暗黙的に呼び出される)。IEnumeratorおよびIEnumerableインターフェイスは、.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およびVB.NET 11は反復子の形でジェネレータ (generator) をサポートする。ジェネレータはIEnumeratorまたはIEnumerableを返すよう宣言されたメソッドであるが、オブジェクトインスタンスを返す代わりに要素のシーケンスを生成するためのyield returnステートメントを使用する。yieldステートメントを用いて記述されたジェネレータはコンパイラによって、適切なインターフェイスを実装する新しいクラスに変換される。ただし、ジェネレータ(反復子)はIEnumerator.Reset()メソッドをサポートしない。

// 反復子の記述例。
static IEnumerable<int> MyIteratorMethod() {
    yield return 1;
    yield return -1;
    yield return 0;
    yield break;
}

IEnumerable<int> elements = MyIteratorMethod(); // この時点では、まだメソッドの本体は実行されない。

foreach (int element in elements)
    Console.WriteLine(element);

Visual Basic 8.0[編集]

' 明示的な使い方
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

D言語[編集]

D言語では、標準ライブラリにレンジ (Range) というイテレータが定義されており、規定されたインターフェイスを持っているオブジェクトなら何でもレンジとして扱うことができる。

foreach (item; range)
	writeln(item);

脚注[編集]

[脚注の使い方]
  1. ^ JISC 日本工業標準調査会 JISX3014「プログラム言語C++」、JISX3015「プログラム言語C#」など。
  2. ^ 繰り返し子(くりかえしし)という訳もあるが一般的ではない。「Rubyプログラミング入門」著者: 原信一郎、出版: オーム社、p.197。
  3. ^ JIS X 3015「プログラム言語C#」p.64より引用。
  4. ^ 反復子 (C#) | Microsoft Docs
  5. ^ yield (C# リファレンス) | Microsoft Docs
  6. ^ yield (C# リファレンス)
  7. ^ IEnumerable・IEnumerator - Programming/.NET Framework/列挙操作と列挙子 - 総武ソフトウェア推進所

関連項目[編集]