【PHP8.x】InfiniteIterator::getInnerIterator()メソッドの使い方
getInnerIteratorメソッドの使い方について、初心者にもわかりやすく解説します。
基本的な使い方
getInnerIteratorメソッドは、InfiniteIteratorオブジェクトが内部で保持している元のイテレータ(インナーイテレータ)を取得するために使用するメソッドです。InfiniteIteratorクラスは、コンストラクタで指定された別のイテレータを、その終端に達しても自動的に巻き戻し、無限に繰り返し処理できるようにするデコレータです。このgetInnerIteratorメソッドを呼び出すと、InfiniteIteratorによってラップされている、つまり飾られている元のイテレータオブジェクトそのものにアクセスすることができます。例えば、ArrayIteratorをInfiniteIteratorでラップして無限ループを実装している途中で、元の配列データを持つArrayIteratorの状態を確認したり、直接操作したりする必要が生じた場合に役立ちます。このメソッドは引数を取らず、返り値として内部に保持されているIteratorインターフェースを実装したオブジェクトを返します。これにより、InfiniteIteratorとしての無限ループの振る舞いとは別に、その基となっているイテレータを直接扱うことが可能になります。
構文(syntax)
1<?php 2 3// 元となるイテレータを作成します 4$arrayIterator = new ArrayIterator(['first', 'second', 'third']); 5 6// InfiniteIteratorで元のイテレータをラップします 7$infiniteIterator = new InfiniteIterator($arrayIterator); 8 9// getInnerIterator() を使って、ラップされている元のイテレータを取得します 10$innerIterator = $infiniteIterator->getInnerIterator(); 11 12// 取得したイテレータのクラス名と、現在の要素を表示します 13var_dump(get_class($innerIterator)); // string(13) "ArrayIterator" 14var_dump($innerIterator->current()); // string(5) "first" 15 16?>
引数(parameters)
引数なし
引数はありません
戻り値(return)
?Iterator
InfiniteIterator インスタンスが内部で保持している Iterator インスタンス、またはそれが設定されていない場合は null を返します。
サンプルコード
PHP InfiniteIterator::getInnerIterator() で内部イテレータを取得する
1<?php 2 3/** 4 * InfiniteIterator::getInnerIterator() の使用例 5 * 6 * このメソッドは、InfiniteIterator がラップしている元の(内部)イテレータを取得します。 7 * InfiniteIterator は、内部のイテレータを無限に繰り返す特性を持つイテレータです。 8 * getInnerIterator() を使用することで、その無限ループから独立して、元のイテレータにアクセスできます。 9 */ 10 11// 1. ラップする元のイテレータ(内部イテレータ)を作成します。 12// ここでは ArrayIterator を使用して簡単な配列を反復処理します。 13$originalArray = ['Alpha', 'Beta', 'Gamma']; 14$innerIterator = new ArrayIterator($originalArray); 15 16echo "元のイテレータ (ArrayIterator) の内容: "; 17foreach ($innerIterator as $item) { 18 echo $item . " "; 19} 20echo PHP_EOL . PHP_EOL; 21 22// 2. 作成した ArrayIterator を InfiniteIterator でラップします。 23// これにより、元のイテレータが無限に繰り返されるようになります。 24$infiniteIterator = new InfiniteIterator($innerIterator); 25 26// 3. InfiniteIterator::getInnerIterator() を呼び出して、 27// ラップされている元のイテレータを取得します。 28$retrievedInnerIterator = $infiniteIterator->getInnerIterator(); 29 30// 4. 取得したイテレータが元の ArrayIterator オブジェクトと同一であるかを確認します。 31// getInnerIterator() は元のオブジェクトの参照を返すため、=== 演算子で同一性が確認できます。 32echo "取得したイテレータは元の ArrayIterator と同じオブジェクトですか? "; 33echo ($retrievedInnerIterator === $innerIterator) ? "はい" : "いいえ"; 34echo PHP_EOL . PHP_EOL; 35 36// 5. 取得した内部イテレータの現在の要素を表示します。 37// InfiniteIterator とは独立して、元のイテレータの状態を操作できます。 38// 安全のためにポインタを先頭に戻します。 39$retrievedInnerIterator->rewind(); 40echo "getInnerIterator() で取得したイテレータの最初の要素: " . $retrievedInnerIterator->current() . PHP_EOL; 41 42// 補足: InfiniteIterator の動作を少しだけ確認します。 43// 無限ループを防ぐため、数回でループを中断します。 44echo "InfiniteIterator の最初の数要素: "; 45$count = 0; 46foreach ($infiniteIterator as $value) { 47 echo $value . " "; 48 $count++; 49 if ($count >= 5) { 50 break; // 無限ループになるため、強制的に終了 51 } 52} 53echo PHP_EOL; 54 55?>
PHP 8のInfiniteIteratorクラスは、他のイテレータ(内部イテレータ)を繰り返し、その要素を無限に提供する特殊なイテレータです。InfiniteIterator::getInnerIterator()メソッドは、このInfiniteIteratorが無限に繰り返している「元の」イテレータを取得するために使われます。
このメソッドは引数を取りません。戻り値は?Iterator型で、InfiniteIteratorがラップしている元のイテレータオブジェクト、または何らかの理由でイテレータがない場合にnullを返します。通常、元のイテレータは必ず存在するため、Iteratorインターフェースを実装したオブジェクトが返されます。
サンプルコードでは、まず配列を扱うArrayIteratorを作成し、これをInfiniteIteratorでラップしています。getInnerIterator()を呼び出すことで、このラップされたArrayIteratorのインスタンスを正確に取得できることを示しています。取得したイテレータは、元のオブジェクトと完全に同一であり、InfiniteIteratorの無限ループの動作とは関係なく、独立してその状態(例えば、現在の要素やポインタ位置)を操作することが可能です。これにより、無限ループの状況から離れて、元のデータの状態を確認したり、リセットしたりといった柔軟な制御が行えるようになります。
InfiniteIterator::getInnerIterator()は、InfiniteIteratorが無限に繰り返す元のイテレータオブジェクトを取得する際に使用します。このメソッドが返すのは、元のイテレータの「コピー」ではなく「参照」である点が特に重要です。そのため、取得したイテレータに対してポインタを動かすなどの操作を行うと、InfiniteIteratorが参照している元のイテレータの状態も直接変化します。これにより、予期せぬ挙動を引き起こす可能性があるため、状態変更の際は注意が必要です。また、InfiniteIterator自体をforeachで利用する際には、無限ループに陥らないよう、必ず適切な終了条件(例えばbreak文)を設定してください。本メソッドは、無限ループから独立して元のイテレータのデータや状態にアクセスしたい場合に役立ちます。
PHP InfiniteIterator の内部イテレータ取得
1<?php 2 3/** 4 * 有限の要素を生成するシンプルなジェネレータ関数です。 5 * InfiniteIteratorが内部でこのジェネレータを複数回巻き戻して再利用する様子を示します。 6 * 7 * @return Generator 8 */ 9function finiteGenerator(): Generator 10{ 11 // ジェネレータが開始または巻き戻されたときにメッセージを表示します。 12 echo "[ジェネレータ] 新しいシーケンスを開始/巻き戻し中..." . PHP_EOL; 13 for ($i = 1; $i <= 3; $i++) { 14 yield "Genアイテム " . $i; 15 } 16 echo "[ジェネレータ] シーケンス終了。" . PHP_EOL; 17} 18 19// 1. オリジナルのジェネレータ(イテレータ)を準備します。 20$originalGenerator = finiteGenerator(); 21 22echo "--- InfiniteIterator の動作デモンストレーション ---" . PHP_EOL; 23 24// 2. InfiniteIterator を使って、この有限のジェネレータを無限にループさせます。 25// InfiniteIterator は内部のイテレータが終端に達すると、自動的に巻き戻して最初から再度イテレートします。 26$infiniteIterator = new InfiniteIterator($originalGenerator); 27 28$count = 0; 29echo "InfiniteIteratorから合計7つの要素を取り出します (ジェネレータは複数回巻き戻されます):" . PHP_EOL; 30foreach ($infiniteIterator as $item) { 31 echo " " . $item . PHP_EOL; 32 $count++; 33 if ($count >= 7) { 34 // 無限ループにならないように、数回で break します。 35 break; 36 } 37} 38echo "--- InfiniteIterator 動作デモンストレーション終了 ---" . PHP_EOL . PHP_EOL; 39 40 41echo "--- InfiniteIterator::getInnerIterator() の動作デモンストレーション ---" . PHP_EOL; 42 43// 3. getInnerIterator() を使用して、InfiniteIterator に渡した元のイテレータ(ジェネレータ)を取得します。 44// このメソッドの戻り値の型は `?Iterator` (PHP 8) ですが、 45// InfiniteIterator のコンストラクタは `Iterator` 型を要求するため、 46// ここでは常に `Iterator` インスタンスが返されます。 47$retrievedIterator = $infiniteIterator->getInnerIterator(); 48 49// 4. 取得したイテレータが元のイテレータと同一インスタンスであることを確認します。 50echo "元のジェネレータのクラス: " . get_class($originalGenerator) . PHP_EOL; 51echo "取得したイテレータのクラス: " . get_class($retrievedIterator) . PHP_EOL; 52 53if ($originalGenerator === $retrievedIterator) { 54 echo "=> 取得したイテレータは、元のジェネレータと完全に同一のインスタンスです。" . PHP_EOL; 55} else { 56 // 通常、InfiniteIteratorに対してこの状況は発生しません。 57 echo "=> 取得したイテレータは、元のジェネレータと異なるインスタンスです。" . PHP_EOL; 58} 59 60// 取得したイテレータの状態を確認します。 61// InfiniteIteratorがすでに元のジェネレータを複数回消費していますが、 62// getInnerIterator()で取得されたイテレータは再度イテレート可能です。 63// InfiniteIteratorが内部で rewind() を呼び出すため、再度最初の状態に戻ります。 64echo "取得したイテレータを再度イテレートしてみます (巻き戻されて最初の状態に戻ります):" . PHP_EOL; 65foreach ($retrievedIterator as $item) { 66 echo " (取得) " . $item . PHP_EOL; 67} 68echo "--- getInnerIterator() 動作デモンストレーション終了 ---" . PHP_EOL;
PHPのInfiniteIteratorクラスは、有限の要素を持つイテレータ(今回の例ではジェネレータ)を、終端に達しても自動的に巻き戻して繰り返し利用することで、まるで無限に続くかのように扱うことができる特殊なイテレータです。
InfiniteIterator::getInnerIterator()メソッドは、このInfiniteIteratorオブジェクトが内部で保持し、繰り返し利用している「元のイテレータ」を取得するために使用されます。このメソッドは引数を必要としません。戻り値の型はPHP 8では?Iterator(Iterator型またはnull)と定義されていますが、InfiniteIteratorのコンストラクタは必ずIterator型のインスタンスを受け取るため、このメソッドは常にIteratorのインスタンスを返します。
サンプルコードでは、まずfiniteGeneratorというジェネレータ関数で有限の要素を生成し、これをInfiniteIteratorに渡しています。InfiniteIteratorは、ジェネレータの要素を消費し尽くすと、自動的にそのジェネレータを巻き戻して再度利用することで、無限ループのように要素を提供します。その後、getInnerIterator()を呼び出すと、InfiniteIteratorが内部で利用していた元のfiniteGeneratorのインスタンスが取得されます。この取得されたイテレータは、元のジェネレータと全く同じインスタンスであり、InfiniteIteratorによって巻き戻された状態(最初の要素を指す状態)に戻っているため、再び最初からイテレートできることを確認できます。このメソッドにより、ラップされた元のイテレータに直接アクセスし、その状態の確認や再利用が可能になります。
InfiniteIterator::getInnerIterator()の利用に関する注意点です。
まず、InfiniteIteratorは内部のイテレータを無限に繰り返すため、サンプルコードのように必ずbreakなどの終了条件を設定しないと、意図しない無限ループが発生する可能性があります。
getInnerIterator()メソッドは、InfiniteIteratorのコンストラクタに渡した元のイテレータ(ジェネレータ)と全く同じインスタンスを返します。コピーではないため、取得したイテレータに対する操作は元のイテレータに直接影響します。
また、取得したイテレータは、InfiniteIteratorが内部でrewind()を呼び出すため、常に最初の状態にリセットされた状態で提供されます。これにより、InfiniteIteratorがどれだけイテレートを進めていても、getInnerIterator()で取得したイテレータはいつでも最初から再利用可能です。
PHP 8以降で戻り値の型が?Iterator(null許容)とされていますが、InfiniteIteratorのコンストラクタはIteratorインスタンスを必須とするため、このメソッドがnullを返すことは通常ありません。常に有効なIteratorインスタンスが返されると理解してください。