イテレータ

コンピュータプログラミングにおいて、イテレータとは、コレクション内の各項目に順番にアクセスを提供するオブジェクトである。[ 1 ] [ 2 ] [ 3 ]

コレクションは、インターフェースを介して、前方や後方など異なる順序で項目を提供する複数の反復子を提供する場合があります。

反復子は、多くの場合、コレクション実装の基礎となる構造に基づいて実装され、反復子の操作セマンティクスを有効にするためにコレクションと 密接に結合されます。

イテレータの動作はデータベース カーソルに似ています。

イテレータは1974年のCLUプログラミング言語にまで遡ります。 [ 4 ]

パターン

イテレータは、コレクションの要素へのアクセス(要素アクセス)を提供し、内部状態を変更することで次の要素へのアクセス(要素トラバーサル)を提供することができます。[ 5 ]また、最初の要素の作成と初期化を提供し、すべての要素がトラバースされたかどうかを示します。プログラミングコンテキストによっては、イテレータは追加の機能を提供します。

イテレータは、消費者がコレクションの内部構造から分離しながら、コレクションの各要素を処理することを可能にします。[ 2 ]コレクションは任意の方法で要素を格納できますが、消費者はシーケンスとして要素にアクセスできます。

オブジェクト指向プログラミングでは、イテレータクラスは通常、対応するコレクションクラスと緊密に連携して設計されます。通常、コレクションはイテレータを作成するためのメソッドを提供します。

ループカウンタはループイテレータと呼ばれることもあります。ただし、ループカウンタはトラバーサル機能のみを提供し、要素アクセス機能は提供しません。

ジェネレータ

イテレータを実装する方法の一つは、ジェネレータと呼ばれる制限された形式のコルーチンを使うことです。サブルーチンとは対照的に、ジェネレータ・コルーチンは呼び出し元に一度だけ値を返すのではなく、複数回値を返すことができます。ほとんどのイテレータは自然にジェネレータとして表現できますが、ジェネレータは呼び出し間でローカルな状態を保持するため、ツリー・トラバーサのような複雑で状態のあるイテレータに特に適しています。「ジェネレータ」と「イテレータ」という用語の使い方には微妙な違いがあり、作者や言語によって異なります。[ 6 ] Pythonでは、ジェネレータはイテレータ・コンストラクタ、つまりイテレータを返す関数です以下は、 Pythonのステートメント を使ってフィボナッチ数列のイテレータを返すPythonジェネレータの例です。yield

入力からインポートジェネレータdef fibonacci ( limit : int ) -> Generator [ int , None , None ]: a , b = 0 , 1 for _ in range ( limit ): yield a a , b = b , a + bfor number in fibonacci ( 100 ): # ジェネレータはイテレータを構築しますprint ( number )

内部イテレータ

内部イテレータは、コレクションを走査しながら各要素に関数を適用する高階関数(多くの場合、匿名関数を引数に取る)です。例えば、Pythonのmap関数は、呼び出し元が定義した関数を各要素に適用します。

入力からインポートIterator数字:リスト[ int ] = [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ]squared_digits : Iterator [ int ] = map ( lambda x : x ** 2 , digits ) # この反復子を反復すると、0、1、4、9、16、...、81 が返されます。

暗黙の反復子

C#C++(後期バージョン)、Delphi(後期バージョン)、GoJava(後期バージョン)、LuaPerlPythonRubyなどの一部のオブジェクト指向言語では、明示的なイテレータを使わずにコレクションの要素を反復処理する組み込みの方法を提供しています。イテレータオブジェクトが存在する場合もありますが、ソースコードには表現されていません。[ 5 ] [ 7 ]

暗黙的な反復子は、多くの場合、言語構文では として現れますforeach

Python では、コレクション オブジェクトを直接反復処理できます。

反復可能オブジェクトの場合: print ( value )

Ruby では、反復処理には iterator プロパティにアクセスする必要があります。

反復可能それぞれ実行||を置く終了

この反復スタイルは、そのコードが反復可能なオブジェクト (反復のすべての側面を制御する) のコンテキスト内で完全に実行され、プログラマーは各ステップで実行する操作 (匿名関数を使用) のみを提供するため、「内部反復」と呼ばれることもあります。

リストの内包表記や同様の構造をサポートする言語では、Python のように、結果リストの構築中に暗黙的な反復子を使用することもできます。

names : list [ str ] = [ person .名簿上の人物名前( person男性の場合) ]

暗黙的な隠蔽の性質は部分的にしか存在しない場合もあります。C ++言語には、 のような暗黙的な反復処理のための関数テンプレートがいくつfor_each()かあります。これらの関数は、初期入力として明示的な反復処理オブジェクトを必要としますが、後続の反復処理では反復処理オブジェクトがユーザーに公開されることはありません。

ストリーム

イテレータは入力ストリームの便利な抽象化であり、潜在的に無限に反復可能な(ただし必ずしもインデックス可能ではない)オブジェクトを提供します。PerlやPythonなど、いくつかの言語では、ストリームをイテレータとして実装しています。Pythonでは、イテレータはデータストリームを表すオブジェクトです。[ 8 ]ストリームの代替実装としては、 AWKsedなどのデータ駆動型言語があります。

インデックスとの対比

多くの言語では、イテレータの代わりに、添字演算子とループカウンタを使って各要素にアクセスすることができます。コレクションではインデックスも使用できますが、イテレータを使用すると以下のような利点があります。[ 9 ]

  • カウント ループはすべてのデータ構造に適しているわけではありません。特に、リストツリーなど、ランダム アクセスがまったくないか、ランダム アクセスが遅いデータ構造には適していません。
  • 反復子は、あらゆる種類のデータ構造を反復処理するための一貫した方法を提供できるため、コードの可読性と再利用性が向上し、データ構造の変更による影響が少なくなります。
  • イテレータは、要素をスキップできないようにしたり、以前にアクセスした要素に 2 度目にアクセスできないようにするなど、アクセスに対する追加の制限を適用できます。
  • イテレータは、イテレータ自体を無効にすることなくコレクションオブジェクトを変更できる場合があります。例えば、イテレータが最初の要素を超えた後、コレクションの先頭にさらに要素を挿入して、予測可能な結果を​​得ることが可能です。しかし、インデックス付けでは、インデックス番号が変更されるため、これは問題となります。

コレクションの要素を反復処理しながら変更できる機能は、オブジェクト間の相互関係や操作の影響が明確でない場合がある現代のオブジェクト指向プログラミングにおいて必須となっています。イテレータを使用することで、こうした影響から隔離されます。しかし、この主張は鵜呑みにすべきではありません。なぜなら、効率性上の理由から、イテレータの実装はコレクションに非常に密接に結びついており、イテレータ自身を無効にすることなく、基になるコレクションを変更できないことがよくあるからです。

メモリ内でデータを移動する可能性のあるコレクションの場合、イテレータを無効化しない唯一の方法は、コレクションが何らかの方法で現在有効なすべてのイテレータを追跡し、それらをオンザフライで更新することです。ある時点でのイテレータの数は、結び付けられたコレクションのサイズと比較して任意に大きくなる可能性があるため、すべてのイテレータを更新すると、コレクションの操作における計算量保証が大幅に損なわれます。

コレクションのサイズに応じて更新回数を制限する別の方法としては、一種のハンドル機構、つまりコレクション内で更新する必要がある要素への間接ポインタの集合を使用し、反復子がデータ要素を直接参照するのではなく、これらのハンドルを参照するようにする方法があります。しかし、このアプローチは、実際のデータ要素にアクセスするためにダブルポインタを経由する必要があるため、反復子のパフォーマンスに悪影響を与えます。反復子を使用する多くのアルゴリズムは、アドバンスメソッドよりも反復子のデータアクセス操作を頻繁に呼び出すため、これは通常望ましくありません。したがって、非常に効率的なデータアクセスを備えた反復子を使用することが特に重要です。

結局のところ、これはセキュリティ(イテレータは常に有効)と効率性の間のトレードオフです。多くの場合、セキュリティの向上は、それに伴う効率性の犠牲に見合うものではありません。イテレータの安定性が必要な場合は、代替コレクション(例えば、ベクターではなく片方向リンクリスト)を使用する方が(全体的に効率が高い)より良い選択肢となります。

分類

カテゴリー

イテレータは機能に応じて分類できます。以下にイテレータの分類の一覧を示します(ただし、網羅的なものではありません)。[ 10 ] [ 11 ]

カテゴリ言語
双方向イテレータC++Rust [ 12 ]
前方イテレータC++
入力イテレータC++
出力イテレータC++
ランダムアクセスイテレータC++
単純な反復子C++(旧STL[ 13 ]

種類

様々な言語やこれらの言語で使用されるライブラリでは、イテレータ型が定義されています。そのいくつかは以下のとおりです。[ 14 ]

タイプ言語
配列イテレータPHP、R [ 15 ]
キャッシュイテレータPHP
定数イテレータC++[ 16 ] PHP
ディレクトリ反復子PHP、Python
フィルタ反復子PHP、R
制限反復子PHP
リスト反復子C#Java[ 7 ] R
再帰配列イテレータPHP
XMLイテレータPHP

さまざまなプログラミング言語

。ネット

.NET Framework (C#など)における反復子は「列挙子」と呼ばれ、IEnumeratorインターフェースによって表されます。[ 17 ] : 189–190, 344 [ 18 ] : 53–54 は、次の要素に進み、コレクションの末尾に達したかどうかを示すメソッドIEnumeratorを提供します。 [ 17 ] : 344 [ 18 ] : 55–56 [ 19 ] : 89 は、現在指している要素の値を取得するプロパティを提供します。[ 17 ] : 344 [ 18 ] : 56 [ 19 ] : 89 は 、 列挙子を初期位置に戻すオプションのメソッドを提供します。列挙子は最初最初の要素の前の特別な値を指し示すため、反復処理を開始するには を呼び出す必要があります。 MoveNext()CurrentReset()MoveNext()

GetEnumerator()列挙子は通常、インターフェースを実装するオブジェクトのメソッドを呼び出すことによって取得されますIEnumerable[ 18 ] : 54–56 [ 19 ] : 54–56 プロパティCurrentを使用して、現在指している要素の値を取得します。[ 17 ] : 344 [ 18 ] : 56 [ 19 ] : 89 コンテナクラスは通常、このインターフェースを実装します。ただし、C#foreach文は、そのようなメソッドを提供する任意のオブジェクト(実装していなくても)に対して操作できます(ダックタイピング)。[ 19 ] : 89 両方のインターフェースは、 .NET 2.0でジェネリックバージョンに拡張されました。 IEnumerable

以下は、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オブジェクト インスタンスを返す代わりに " " ステートメントを使用して要素のシーケンスを生成するメソッドは、コンパイラによって適切なインターフェイスを実装する新しいクラスに変換されます。

C

イテレータはCにはネイティブでは存在しませんが、ポインタ演算を使用してある程度エミュレートできます。

以下は、反復子を使用したリンク リストを示していますint

typedef struct LinkedList { int * value ; struct LinkedList * next ; } LinkedList ;typedef struct { LinkedList * current ; } LinkedListIterator ;LinkedListIterator list_begin ( LinkedList * head ) { LinkedListIterator it = { head };それを返します; }int * list_next ( LinkedListIterator * it ) { if ( ! it -> current ) { return NULL ; } int * value = & it -> current -> value ; it -> current = it -> current -> next ; return value ; }int main () { // ... for ( LinkedListIterator it = list_begin ( head ); ; ) { int * val = list_next ( & it ); if ( ! val ) { break ; } printf ( "%d \n " , * val ); } }

C++

C ++言語では、標準ライブラリで反復子を幅広く使用しており、実行可能な操作のレパートリーが異なる複数の反復子のカテゴリが定義されています。これらには、可能性が増す順に、順方向反復子双方向反復子ランダム アクセス反復子が含まれます。すべての標準コンテナー テンプレート型は、これらのカテゴリのいずれかの反復子を提供します。反復子は、配列の要素 (反復子として実際に使用できます) へのポインターを一般化します。その構文は、Cポインター算術に似せて設計されています。C ポインター算術では、演算子*->演算子を使用して、反復子が指す要素を参照し、のようなポインター算術演算子を++使用して、コンテナーのトラバースで反復子を変更します。

イテレータを用いたトラバーサルでは、通常、1つの可変イテレータと、トラバースする範囲を区切る2つの固定イテレータが使用されます。++下限値を上限値に変換するために必要な演算子の適用回数で表した、制限イテレータ間の距離は、指定範囲内の項目数に等しくなります。使用される異なるイテレータ値の数は、それより1つ多くなります。慣例的に、下限イテレータは範囲の最初の要素を「指し示し」、上限イテレータは範囲内のどの要素も指さず、範囲の終端を少し超えた位置を指します。コンテナ全体のトラバーサルでは、メソッドはbegin()下限値とend()上限値を提供します。上限値はコンテナ内のどの要素も参照しませんが、比較可能な有効なイテレータ値です。

次の例は、反復子の一般的な使用方法を示しています。

stdをインポートしますstd :: vectorを使用しますint main () { vector < int > items ; items.push_back ( 5 ) ; // ベクター 'items' に整数値 '5' を追加します。items.push_back ( 2 ) ; //ベクター'items' に整数値 '2' を追加します。items.push_back ( 9 ) ; // ベクター 'items' に整数値 '9' を追加します// 'items' を反復処理します。// 'it' は vector<int>::iterator 型です。for ( auto it = items . begin (), end = items . end (); it != end ; ++ it ) { // 現在のインデックスの 'items' の値を出力します。std :: print ( "{}" , * it ); }// foreach ループ (C++11 で追加) を使用すると、明示的な反復子を使用せずに同じことを行うことができます: for ( int x : items ) { // 'items' の各要素 'x' の値を出力します。std :: print ( " {}" , x ); }// 両方の for ループは "529" を出力します。}

イテレータ型は、それらが使用されるコンテナ型とは別のものですが、両者はしばしば一緒に使用されます。イテレータのカテゴリ(およびそれに定義される演算)は通常、コンテナの型によって異なります。例えば、配列やベクターはランダムアクセスイテレータを提供しますが、集合(実装としてリンク構造を使用する)は双方向イテレータのみを提供します。同じコンテナ型には、複数のイテレータ型が関連付けられる場合があります。例えば、コンテナstd::vector<T>型では、その要素(型 )への(生の)ポインタT*、または特殊な型 の値のいずれかを使用してトラバーサルを実行できますstd::vector<T>::iterator。また、「逆イテレータ」用の型も用意されています。逆イテレータの演算は、通常の(順方向)トラバーサルを実行するアルゴリズムが、逆イテレータで呼び出された場合、実際には逆順にトラバーサルを実行するように定義されています。ほとんどのコンテナは、別のconst_iterator型も提供していますが、その型では、指し示す値を変更できる演算は意図的に定義されていません。

コンテナ オブジェクトまたはその要素の範囲の単純な走査 ( がconst_iterator使用されない限り、それらの要素の変更を含む) は、反復子だけを使用して実行できます。ただし、コンテナ型は、コンテナ自体の構造を変更するinsertや などのメソッドも提供することがありますerase。これらはコンテナ クラスのメソッドですが、目的の操作を指定するために 1 つ以上の反復子値も必要とします。複数の反復子が同時に同じコンテナを指すことは可能ですが、構造を変更する操作によって特定の反復子値が無効になることがあります (標準では、各ケースについて、これが可能かどうかを指定します)。無効になった反復子を使用すると、未定義の動作につながるエラーが発生します。このようなエラーは、ランタイム システムによって通知される必要はありません。

std::for_each()暗黙的な反復処理は、、、 std::copy() など の標準関数テンプレートを使用することで、C++ でも部分的にサポートされますstd::accumulate()

beginこれらを使用する場合、既存のイテレータ(通常はと)で初期化する必要があります。endこれらのイテレータは、反復処理が行われる範囲を定義します。ただし、反復処理が進行するにつれて、明示的なイテレータオブジェクトは公開されません。この例は の使用法を示していますfor_each

// ItemType 要素の任意の標準コンテナー タイプ。ContainerType < ItemType > c ;// コレクションの各項目を処理する関数。void processItem ( const ItemType & i ) { std :: println ( "{}" , i ) ; }// for-each 反復ループ。std :: for_each ( c . begin (), c . end (), processItem ); // std::ranges を使用: std :: ranges :: for_each ( c , processItem );

同じことは、 を使ってstd::copystd::ostream_iterator値を 3 番目の反復子として渡すことでも実現できます。

std :: coutを使用します。std :: ostream_iterator使用しますstd :: copy ( c . begin ()、c . end ()、ostream_iterator < ItemType > ( cout " \n " )); // std::ranges を使用します: std :: ranges :: copy ( c ostream_iterator < ItemType > ( cout " \n " ));

C++11以降、ラムダ関数構文を使用してインラインで反復処理する操作を指定できるようになり、名前付き関数を定義する必要がなくなりました。以下は、ラムダ関数を用いたfor-each反復処理の例です。

ContainerType < ItemType > c ; // ItemType 要素の任意の標準コンテナー タイプ。// ラムダ関数を使用した for-each 反復ループ。std :: for_each ( c . begin ( ), c . end (), []( const ItemType & i ) -> void { std :: println ( "{}" , i ); });// std::ranges を使用します: std :: ranges :: for_each ( c , []( const ItemType & i ) -> void { std :: println ( "{}" , i ); });

反復子の Java スタイルの構文 ( Iterator<T>、 vs T::iterator) は、C++ では次のように実装できます。

stdをインポートしますstd :: conditional_tを使用しますnamespace { // ヘルパー概念: iterator が 'iterator' として定義されている場合template < typename T > concept Has_iterator = requires { typename T :: iterator };// ヘルパー概念: イテレータが 'Iterator' として定義されている場合template < typename T > concept Has_Iterator = requires { typename T :: Iterator }; }template < typename Iterable >にはHas_iterator < Iterable > || Has_Iterator < Iterable >が必要です。Iterator = conditional_t < Has_Iterator < Iterable > typename Iterable :: Iterator typename Iterable :: iterator >を使用します// は次のように呼び出すことができます:テンプレート< typename T >でVector = std :: vector < T >を使用しますint main () { Vector < int > v { 1 , 2 , 3 , 4 , 5 }; for ( Iterator < Vector < int >> it ; it != v . end (); ++ it ) { std :: println ( "{}" , * it ); } }

ジャワ

Java JDK 1.2リリースで導入されたこのjava.util.Iteratorインタフェースは、コンテナクラスの反復処理を可能にします。各コンテナクラスはメソッド[ 20 ]と[ 294–295 Iterator ]を提供し、オプションで[ 20 ] :262,266 メソッドをサポートすることもできます。反復子は対応するコンテナクラスによって、通常は[21]というメソッドによって作成されます。[ 21 ] [ 20 ] :99 [ 20 ] :217 next()hasNext()remove()iterator()

メソッドはnext()イテレータを進め、イテレータが指す値を返します。最初の要素は、 の最初の呼び出し時に取得されますnext()[ 20 ] : 294–295 コンテナ内のすべての要素が参照されたかどうかを判断するには、hasNext()テストメソッドを使用します。[ 20 ] : 262 次の例は、イテレータの簡単な使用方法を示しています。

イテレータiter = list . iterator ();イテレータ< MyType > iter = list . iterator (); // イテレータ iter = list.iterator(); J2SE 5.0 より前while ( iter . hasNext ()) { System . out . print ( iter . next ()); if ( iter . hasNext ()) { System . out . print ( ", " ); } }

hasNext()繰り返し呼び出せる ことを示すために、最後の要素の後ではなく、要素間にコンマを挿入するためにこれを使用します。

このアプローチでは、アドバンス操作と実際のデータアクセスが適切に分離されていません。データ要素をアドバンスごとに複数回使用する必要がある場合は、一時変数に格納する必要があります。データアクセスなしでアドバンス操作が必要な場合(つまり、特定のデータ要素をスキップする場合)、アクセスは実行されますが、この場合、戻り値は無視されます。

コレクション型がイテレータをサポートする場合、remove()イテレータのメソッドは、イテレータを使用可能な状態に保ちながら、コンテナから最後にアクセスした要素を削除します。コンテナのメソッド(同じスレッドhasNext()からのものも含む)を呼び出して要素を追加または削除すると、イテレータは使用できなくなります。次の要素を取得しようとすると例外がスローされます。また、要素が残っていない場合(つまり、 が以前にfalseを返した場合) にも例外がスローされます。

さらに、同様の API を持つjava.util.Listがありますjava.util.ListIteratorが、前方および後方への反復が可能で、リスト内の現在のインデックスを提供し、その位置にリスト要素を設定できます。

JavaのJ2SE 5.0リリースでは、コレクションと配列を反復処理するための拡張(foreach)ループをサポートするインタフェースが導入されました。Iterablefor返すメソッドをIterable定義します。[ 20 ] : 266 拡張ループを使用すると、前の例は次のように書き直すことができます。 iterator()Iteratorfor

for ( MyType obj : list ) { System . out . print ( obj ); }

一部のコンテナは、古いEnumerationクラス(1.0以降)も使用します。このクラスはメソッドhasMoreElements()nextElement()メソッドを提供しますが、コンテナを変更するメソッドはありません。

スカラ

Scalaでは、イテレータはコレクションと同様に豊富なメソッドを備えており、 for ループで直接使用できます。実際、イテレータとコレクションはどちらも共通のベーストレイトである を継承しています。しかし、Scala コレクションライブラリには、などscala.collection.TraversableOnce豊富なメソッドが用意されているため、Scala でプログラミングする際にイテレータを直接扱う必要はほとんどありません。 mapcollectfilter

Javaのイテレータとコレクションは、次の1行を追加するだけで、それぞれScalaのイテレータとコレクションに自動的に変換できます。

scala.collection.JavaConversions._インポートます

ファイルに追加します。JavaConversionsオブジェクトは、これを行うための暗黙的な変換を提供します。暗黙的な変換はScalaの機能です。現在のスコープ内で参照可能なメソッドは、関連する式に適切な場所に自分自身の呼び出しを自動的に挿入し、通常は型チェックされないメソッドを型チェックします。

MATLAB

MATLABは、「ネイティブ」配列または配列を用いた外部反復と内部反復の両方をサポートしていますcell。外部反復の場合、ユーザーが探索を進めて次の要素を要求する必要があるため、配列ストレージ構造内に要素の集合を定義し、for-loop構造を用いて要素を探索することができます。例えば、

% 整数の配列を定義します。myArray = [ 1 , 3 , 5 , 7 , 11 , 13 ];for n = myArray % ... n で何かするdisp ( n ) % コマンドウィンドウに整数をエコーするend

キーワードを使用して整数の配列を走査しますfor

ユーザーが反復子にコレクションのすべての要素に対して演算を実行させる内部反復処理の場合、多くの組み込み演算子とMATLAB関数はオーバーロードされ、配列のすべての要素に対して実行され、対応する出力配列を暗黙的に返します。さらに、関数arrayfuncellfun関数は、それぞれ「ネイティブ」配列と配列に対してカスタムまたはユーザー定義の演算を実行するために利用できますcell。例えば、

function simpleFun % 整数の配列を定義します。myArray = [ 1 , 3 , 5 , 7 , 11 , 13 ];% 各要素に対してカスタム操作を実行します。myNewArray = arrayfun (@( a ) myCustomFun ( a ), myArray );% 結果の配列をコマンドウィンドウにエコーしますmyNewArrayfunction outScalar = myCustomFun ( inScalar ) % 単純に 2 を掛けますoutScalar = 2 * inScalar ;

組み込み関数を使用して、配列の各要素にsimpleFunカスタムサブ関数を暗黙的に適用する基本関数を定義します。 myCustomFunarrayfun

あるいは、反復子パターンのカスタムオブジェクト指向MATLAB実装を定義することで、配列ストレージコンテナのメカニズムをユーザーから抽象化することが望ましい場合があります。外部反復をサポートするこのような実装は、MATLAB Central File Exchangeの項目「デザインパターン:反復子(ビヘイビア)」で示されています。これは、MATLABソフトウェアバージョン7.6(R2008a)で導入された新しいクラス定義構文で記述されており、異種(データ型)の要素セットを格納するメカニズムとして、リスト抽象データ型cell(ADT)の1次元配列実現を備えています。これは、、、およびループで使用するメソッドを使用して、リストを明示的に前方にトラバーサルする機能を提供します。 hasNext()next()reset()while

PHP

PHP の Iterator インターフェースの UML クラス図
PHP の Iterator インターフェースの UML クラス図

PHPforeachループはバージョン 4.0 で導入され、4.0 Beta 4 で値としてのオブジェクトと互換性を持つようになりました。[ 22 ]しかし、反復子のサポートは PHP 5 で内部[ 23 ]Traversableインターフェースの導入により追加されました。 [ 24 ]foreachループを介してオブジェクトの反復処理を可能にする PHP スクリプトの実装用の主な 2 つのインターフェースはIteratorとですIteratorAggregate。後者は実装クラスですべての必要なメソッドを宣言する必要はなく、代わりにのインスタンスを返すアクセサメソッド ( ) を実装します。標準 PHP ライブラリでは、特殊な反復子を操作するためのクラスがいくつか提供されています。[ 25 ] PHP は 5.5 以降でジェネレーターもサポートしています。[ 26 ]getIteratorTraversable

最も単純な実装は配列をラップすることです。これは型のヒント情報の隠蔽に役立ちます。

名前空間Wikipedia\Iterator ;finalクラスArrayIterator は\Iteratorを拡張します{ private array $array ;パブリック関数__construct (配列$array ) { $this -> array = $array ; }パブリック関数rewind () : void { echo 'rewinding' , PHP_EOL ; reset ( $this -> array ); }パブリック関数current () { $value = current ( $this -> array ); echo "current: { $value } " , PHP_EOL ; return $value ; }公開関数キー(){ $key = key $this -> array ); echo "key: { $key } " PHP_EOL ; return $key ; }パブリック関数next () { $value = next ( $this -> array ); echo "next: { $value } " , PHP_EOL ; return $value ; }パブリック関数valid () : bool { $valid = $this -> current () !== false ; echo 'valid: ' , ( $valid ? 'true' : 'false' ), PHP_EOL ; return $valid ; } }

サンプルクラスのすべてのメソッドは、完全な foreach ループ ( ) の実行中に使用されますforeach ($iterator as $key => $current) {}。イテレータのメソッドは以下の順序で実行されます。

  1. $iterator->rewind()内部構造が最初から始まることを保証します。
  2. $iterator->valid()この例ではtrueを返します。
  3. $iterator->current()返された値は に格納されます$value
  4. $iterator->key()返された値は に格納されます$key
  5. $iterator->next()内部構造の次の要素に進みます。
  6. $iterator->valid()falseを返してループを中止します。

次の例は、このインターフェースを実装したPHPクラスを示しています。このクラスをクラスTraversableにラップすることで、ループIteratorIteratorに返される前にデータを処理することが可能ですforeach。定数と併用することで、MYSQLI_USE_RESULTPHPスクリプトは数十億行の結果セットを非常に少ないメモリ使用量で反復処理できます。これらの機能は、PHPやMySQLのクラス実装に限ったものではありません(例えば、PDOStatementクラスもこのインターフェースを実装していますTraversable)。

mysqli_report ( MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT ); $mysqli = new \mysqli ( 'host.example.com' 'ユーザー名' 'パスワード' 'データベース名' );// メソッド呼び出しによって返される \mysqli_result クラスは、内部の Traversable インターフェイスを実装します。foreach ( $mysqli -> query ( 'SELECT `a`, `b`, `c` FROM `table`' , MYSQLI_USE_RESULT ) as $row ) { // 返された行 (連想配列) に対して操作を行います。}

パイソン

Pythonにおけるイテレータは言語の基本的な要素であり、for( foreach )文、リスト内包表記ジェネレータ式などで暗黙的に使用されるため、多くの場合は目に見えません。Pythonの標準組み込みコレクション型はすべて、標準ライブラリに含まれる多くのクラスと同様に、イテレーションをサポートしています。次の例は、シーケンスに対する典型的な暗黙的なイテレーションを示しています。

シーケンス内のの場合print ( value )

Python 辞書 (連想配列の一種) は、辞書のキーが返されるときに直接反復処理することもできます。また、items()辞書のメソッドを反復処理して、対応するキーと値のペアをタプルとして生成することもできます。

辞書キー:=辞書[キー] print (キー)
辞書キー.items ()場合: print ( key , value )

ただし、イテレータは明示的に使用および定義できます。反復可能なシーケンス型またはクラスでは、組み込み関数をiter()使用してイテレータオブジェクトを作成します。作成されたイテレータオブジェクトは、関数で反復処理できます。この関数は内部的にnext()メソッドを使用し__next__()、コンテナ内の次の要素を返します。(前の記述はPython 3.xに適用されます。Python 2.xでは、メソッドはnext()同等です。)StopIteration要素が残っていない場合は例外が発生します。次の例は、明示的なイテレータを使用してシーケンスに対して同等の反復処理を実行する例です。

入力からインポートIteratorsequence : list [ int ] = [ 1 , 2 , 3 , 4 ] it : Iterator [ int ] = iter ( sequence ) while True : try : value = it . next () # Python 2.x の場合value = next ( it ) # Python 3.x の場合except StopIteration : break print ( value )

任意のユーザー定義クラスは、__iter__()イテレータオブジェクトを返すメソッドを定義することで、標準的な反復処理(暗黙的または明示的)をサポートできます。次に、イテレータオブジェクトに__next__()次の要素を返すメソッドを定義する必要があります。

Python のジェネレーターはこの反復プロトコルを実装します。

Rakuにおけるイテレータは言語の基本的な部分ですが、通常、ユーザーはイテレータを意識する必要はありません。イテレータの使い方はformapgrep、 によるリストのインデックス付け.[$idx]などのイテレーションAPIの背後に隠されています。

次の例は、値のコレクションに対する典型的な暗黙的な反復を示しています。

my @values = 1 , 2 , 3 ; @values -> $value { $value言う } # 出力: # 1 # 2 # 3

Rakuハッシュは直接反復処理することもでき、キーと値のPairオブジェクトが生成されます。kvハッシュに対してメソッドを呼び出すことで、キーと値を反復処理できます。keysまた、メソッドはハッシュのキーを反復処理し、valuesメソッドはハッシュの値を反復処理します。

my %word-to-number = 'one' => 1'two' => 2'three' => 3 ; for %word-to-number -> $pair { say $pair ; } # 出力: # 3 => 3 # 1 => 1 # 2 => 2% word-to-number . kv -> $key , $value { "$key: $value"とします } # 出力: # 3: 3 # 1: 1 # 2: 2%word-to-numberの場合keys -> $key { say "$key => " ~ %word-to-number { $key }; } # 出力: # 3 => 3 # 1 => 1 # 2 => 2

ただし、イテレータは明示的に使用および定義できます。反復可能な型には、反復処理のさまざまな側面を制御する複数のメソッドがあります。例えば、メソッドはオブジェクトiteratorを返すものとしIteratorpull-oneメソッドは可能な場合は次の値を生成して返すものとし、IterationEndそれ以上値を生成できない場合はセンチネル値を返すものとしています。次の例は、明示的なイテレータを使用してコレクションに対して同等の反復処理を実行する例です。

my @values = 1 , 2 , 3 ; my $it  := @values . iterator ; # @values のイテレータを取得するloop { my $value  := $it . pull-one ; # 反復の次の値を取得しますlast if $value =:= IterationEnd ; # 反復の終了に達したら停止しますsay $value ; } # 出力: # 1 # 2 # 3

Raku のすべての反復可能型は、Iterable役割 、Iterator役割 、またはその両方を構成します。 はIterable非常に単純で、iterator構成クラスで を実装するだけで済みます。 はIteratorより複雑で、 などの一連のメソッドを提供します。これにより、アイテムの追加や削除、アイテムをスキップして他のアイテムにアクセスするなど、さまざまなコンテキストでよりきめ細かな反復処理が可能になります。したがって、これらの役割を組み合わせ、および/またはメソッドpull-oneを実装することで、任意のユーザー定義クラスで標準的な反復処理をサポートできます。 iteratorpull-one

このDNAクラスはDNA鎖を表し、役割iteratorを合成することでを実装しますIterable。DNA鎖は、以下の反復処理によって3ヌクレオチドのグループに分割されます。

StrサブセットStrand where { .match ( /^^ <[ACGT]>+ $$/ ) and .chars %%  3 } ; クラスDNAはIterable { $.chainを持ちます;メソッド new ( Strand :D $chain ) { self .bless :  : $chain } メソッドiterator ( DNA:D: ){ $.chain . comb . rotor ( 3 ). iterator } }; DNA新しい( 'ガタガタ' ) { 。言う } # 出力: # (GAT) # (TAC) # (ATA)say DNA . new ( 'GATTACATA' ). map (*. join ). join ( '-' ); # 出力: # GAT-TAC-ATA

クラスはと のRepeater両方のロールを構成します。 IterableIterator

クラスRepeater はIterableIterator を実行 ます { Any $.item必要です。Int $.times必要です。Int $!count = 1がありますマルチメソッドnew ( $item$times ) { self . bless:  : $item、 : $times ; } メソッドイテレータ{ self } メソッドpull-one (--> Mu ){ if $!count <= $!times { $!count += 1 ; return $!item } そうでない場合は{ 反復終了を返す } } } Repeaterの場合. new ( "Hello" , 3 ) { 。言う } # 出力: # こんにちは# こんにちは# こんにちは

ルビー

Rubyではイテレータの実装が全く異なります。すべての反復処理は、コンテナメソッドにコールバッククロージャを渡すことで実行されます。これにより、Rubyは基本的な反復処理だけでなく、関数マッピング、フィルター、リデュースといった様々な反復処理パターンも実装しています。Rubyは基本的な反復処理メソッドの代替構文もサポートしておりeach、以下の3つの例はどちらも同等です。

( 0 ... 42 ) .それぞれdo | n |はn を終了させます

...そして...

n in 0 ... 42はn を終了せます

あるいはもっと短い

42 .do | n |置くn終了

EnumeratorRuby では、 を使用し、その#nextメソッドを呼び出すか、上記のように for each を実行すること で、固定リストを反復処理することもできます。

さび

Rustは標準ライブラリ全体で外部イテレータを利用しており、forループ内でもその機能が利用されます。ループは、イテレータが消費されるまで暗黙的にイテレータのメソッドを呼び出します。例えば、next()最も基本的なループは型を反復処理します。 forRange

for i in 0 .. 42 { println! ( "{}" , i ); } // 0から41までの数字を出力します

具体的には、forループは値のメソッドを呼び出しinto_iter()、そのメソッドはイテレータを返します。イテレータはループに要素を渡します。ループfor(あるいはイテレータを使用する任意のメソッド)は、next()メソッドが値を返すまで処理を続行しますNone(要素を返す反復処理はSome(T)値を返します。ここでTは要素の型です)。

標準ライブラリが提供するすべてのコレクションは、IntoIteratorトレイトを実装しています(つまり、into_iter()メソッドを定義しています)。イテレータ自体もIteratorトレイトを実装しており、next()メソッドの定義が必要です。さらに、トレイトを実装するすべての型には、自身を返す Iterator実装が自動的に提供されます。IntoIterator

イテレータは、特性によって自動的に提供されるメソッドとして、さまざまなアダプタ ( map()、、、など)をサポートします。 filter()skip()take()Iterator

ユーザーは、トレイトを実装した型を作成することで、カスタムイテレータを作成できますIterator。カスタムコレクションは、このIntoIteratorトレイトを実装し、要素に関連付けられたイテレータ型を返すことで、forループ内で直接使用できるようになります。以下は、このFibonacci型がカスタムの無制限イテレータを実装している例です。

構造体フィボナッチ( u64 , u64 );フィボナッチの実装{ pub fn new () -> Self { Self ( 0 , 1 ) } }フィボナッチイテレータを実装します{ type Item = u64 ;fn next ( & mut self ) -> Option < Self :: Item > { let next = self . 0 ; self . 0 = self . 1 ; self . 1 = self . 0 + next ;いくつか} }fn main () { let fib = Fibonacci :: new (); for n in fib . skip ( 1 ). step_by ( 2 ). take ( 4 ) { println! ( "{n}" ); } // 1、2、5、13 を出力します}

Rust のイテレータ関連の構造体と特性を示す UML クラス図

参照

参考文献

  1. ^ Gatcomb, Joshua (2005年6月16日). 「イテレータの理解と使用」 . Perl.com . 2012年8月8日閲覧。ユーザー定義イテレータは通常、コード参照の形をとり、実行されるとリスト内の次の項目を計算して返します。イテレータがリストの末尾に到達すると、合意された値を返します。
  2. ^ a b Watt, Stephen M. (2006年9月16日). 「汎用反復処理とその最適化のための手法」(PDF) . ウェスタンオンタリオ大学、コンピュータサイエンス学部. 2012年8月8日閲覧.反復子は、抽象データ構造の内部表現を明らかにすることなくループ処理を可能にする構造として導入されました。
  3. ^ Alex Allain. 「STLイテレータ」 . Cprogramming.com - CとC++のリソース. 2012年8月8日閲覧。イテレータは、より大きなアイテムコンテナの一部であるアイテムを指すものと考えることができます。
  4. ^ Liskov, B. ; Snyder, A. ; Atkinson, R. ; Schaffert, C. (1977年8月). 「CLUにおける抽象化メカニズム」. Communications of the ACM . 20 (8): 564– 576. CiteSeerX 10.1.1.112.656 . doi : 10.1145/359763.359789 . S2CID 17343380 .  
  5. ^ a b「外部イテレータと内部イテレータの違い」 . CareerRide.COM. 2009年4月3日. オリジナルから2012年9月19日にアーカイブ2012年8月8日閲覧。内部イテレータは、反復ロジックを持つクラスのメンバー関数によって実装されます。外部イテレータは、反復ロジックを持つオブジェクトにアタッチできる別のクラスによって実装されます。外部イテレータの利点は、既存または同一のオブジェクトに対して複数のイテレータを同時にアクティブにできることです。{{cite web}}: CS1 maint: bot: 元のURLステータス不明(リンク
  6. ^ Watt, Stephen M. 「A Technique for Generic Iteration and Its Optimization」 . ウェスタンオンタリオ大学、コンピュータサイエンス学部。2012年8月6日時点のオリジナルよりアーカイブ。 2012年8月8日閲覧著者によっては「イテレータ」という用語を使用し、「ジェネレータ」という用語を使用する。両者を微妙に区別する人もいる。{{cite web}}: CS1 maint: bot: 元のURLステータス不明(リンク
  7. ^ a bフリーマン, エリック; フリーマン, エリザベス; キャシー, シエラ; バート, ベイツ (2004). ヘンドリクソン, マイク; ルーキデス, マイク (編). Head First Design Patterns (ペーパーバック) . 第1巻. オライリー. p. 338. ISBN 978-0-596-00712-6. 2012年8月9日閲覧
  8. ^ 「用語集 — Python 3.8.4 ドキュメント」2020年7月15日閲覧。
  9. ^ Vecerina, Ivan (2006-02-01). 「インデックス vs イテレータ」 . BYTES. 2012-08-09にオリジナルからアーカイブ。2012-08-08に閲覧。インデックスは、ランダムアクセス(つまり、特定の位置にある要素への直接アクセス)を(効率的に)サポートするコンテナにのみ使用できます。イテレータはより一般的な概念です。イテレータは、リンクリスト、ファイル、その他多くのデータ構造を効率的に走査します。これにより、より効率的なコードが生成されることが少なくありません。{{cite web}}: CS1 maint: bot: 元のURLステータス不明(リンク
  10. ^ Kevin Waterson. 「C++ Iteratoren: Iterator-Kategorien」(ドイツ語). cppreference.com . 2012年8月9日閲覧
  11. ^ Kevin Waterson. 「イテレータ:概念」 . sgi . 2012年8月9日閲覧
  12. ^ "DoubleEndedIterator in std::iter - Rust" . doc.rust-lang.org . 2025年5月11日閲覧
  13. ^ larsmans (2011-03-06). 「イテレータの種類:出力 vs. 入力 vs. 順方向 vs. ランダムアクセスイテレータ」 . stackoverflow. 2012年8月8日時点のオリジナルよりアーカイブ。 2012年8月9日閲覧{{cite web}}: CS1 maint: bot: 元のURLステータス不明(リンク
  14. ^ Kevin Waterson. 「SPL入門:標準PHPライブラリ(SPL)入門」 PHPRO.ORG . 2012年8月9日閲覧
  15. ^ Collier, Andrew. 「Rにおける反復処理」2018年10月18日時点のオリジナルよりアーカイブ2013年11月16日閲覧。
  16. ^ "concurrent_unordered_set テンプレートクラス" . Intel Threading Building Blocks for Open Source. 2015年5月1日時点のオリジナルからのアーカイブ。 2012年8月9日閲覧•イテレータ型 iterator と const_iterator は順方向イテレータのカテゴリに属します。
  17. ^ a b c d eアルバハリ、ジョセフ (2022). C# 10 の概要。オライリー。ISBN 978-1-098-12195-2
  18. ^ a b c d e Skeet, Jon (2019年3月23日). C# in Depth . Manning. ISBN 978-1617294532
  19. ^ a b c d e Price, Mark J. C# 8.0 と .NET Core 3.0 – 最新のクロスプラットフォーム開発: Visual Studio Code を使用して C#、.NET Core、Entity Framework Core、ASP.NET Core、ML.NET でアプリケーションを構築する. Packt. ISBN 978-1-098-12195-2
  20. ^ a b c d e f gブロッホ、ジョシュア(2018年)『Effective Java: Programming Language Guide』(第3版)Addison-Wesley. ISBN 978-0134685991
  21. ^ 「java.util: インタフェースIterator<E>: メソッドの概要」 . Oracle . 2012年8月8日閲覧
  22. ^ 「PHP 4 ChangeLog」 . PHPグループ. 2000年2月20日. 2015年10月13日閲覧
  23. ^内部とは、インターフェースを PHP スクリプトで実装できず、 C (プログラミング言語)ソースでのみ実装できることを意味します。
  24. ^ 「Traversableインターフェース」 PHPグループ。 2015年10月13日閲覧
  25. ^ 「イテレータ」 . PHPグループ. 2015年10月13日閲覧。
  26. ^ 「PHP 5 ChangeLog」 . PHPグループ. 2013年6月20日. 2015年10月13日閲覧