【PHP8.x】IteratorIterator::rewind()メソッドの使い方
rewindメソッドの使い方について、初心者にもわかりやすく解説します。
基本的な使い方
rewindメソッドは、IteratorIteratorオブジェクトが内部で保持しているイテレータを、その先頭に巻き戻す処理を実行するメソッドです。IteratorIteratorは、配列や他のオブジェクトコレクションなどを順番に処理するための仕組みである「イテレータ」を、さらに内包(ラップ)して利用するためのクラスです。このrewindメソッドは、PHP標準のIteratorインターフェースで定められているメソッドの一つであり、繰り返し処理を開始する前に、処理対象のポインタを集合の最初の要素に戻す重要な役割を担います。具体的には、foreach構文でIteratorIteratorオブジェクトをループ処理する場合、PHPエンジンはループが始まる一番最初にこのrewindメソッドを自動的に呼び出します。これにより、内包されたイテレータのポインタが確実に先頭にリセットされ、常にコレクションの最初の要素から処理を開始できることが保証されます。開発者がこのメソッドを直接呼び出す機会は少ないですが、PHPにおけるイテレータの基本的な振る舞いを定義する上で不可欠な機能となっています。
構文(syntax)
1<?php 2 3// イテレートする配列を準備します 4$array = ['A', 'B', 'C']; 5$iterator = new IteratorIterator(new ArrayIterator($array)); 6 7// イテレータを2番目の要素まで進めます 8$iterator->next(); 9// echo $iterator->current(); // "B" 10 11// rewind()メソッドを呼び出して、イテレータを先頭に戻します 12$iterator->rewind(); 13 14// 先頭に戻っていることを確認します 15echo $iterator->current(); // "A" 16
引数(parameters)
引数なし
引数はありません
戻り値(return)
戻り値なし
戻り値はありません
サンプルコード
PHP Generator を巻き戻し可能にする方法
1<?php 2 3/** 4 * ジェネレータをラップし、巻き戻し可能にするためのカスタムイテレータクラス。 5 * 6 * PHPのジェネレータは通常、一度しかイテレーションできません(巻き戻し不可)。 7 * このクラスは、ジェネレータを生成するファクトリ(クロージャ)を保持し、 8 * rewind()メソッドが呼ばれるたびに新しいジェネレータを生成することで、 9 * 擬似的に「巻き戻し」を実現します。 10 */ 11class RewindableGenerator implements Iterator 12{ 13 /** @var Closure ジェネレータを生成するファクトリクロージャ */ 14 private Closure $factory; 15 16 /** @var ?Generator 内部で保持するジェネレータインスタンス */ 17 private ?Generator $generator = null; 18 19 /** 20 * コンストラクタ。 21 * 22 * @param Closure $factory ジェネレータを返すクロージャを受け取る 23 */ 24 public function __construct(Closure $factory) 25 { 26 $this->factory = $factory; 27 } 28 29 /** 30 * イテレータを先頭に巻き戻します。 31 * ファクトリを再実行して、新しいジェネレータインスタンスを生成します。 32 */ 33 public function rewind(): void 34 { 35 // ファクトリクロージャを呼び出し、新しいジェネレータを生成 36 $factory = $this->factory; 37 $this->generator = $factory(); 38 } 39 40 /** 41 * 現在の要素を返します。 42 */ 43 public function current(): mixed 44 { 45 return $this->generator->current(); 46 } 47 48 /** 49 * 現在のキーを返します。 50 */ 51 public function key(): mixed 52 { 53 return $this->generator->key(); 54 } 55 56 /** 57 * 次の要素に進みます。 58 */ 59 public function next(): void 60 { 61 $this->generator->next(); 62 } 63 64 /** 65 * 現在位置が有効かどうかを返します。 66 */ 67 public function valid(): bool 68 { 69 // rewind()が呼ばれていない初期状態ではジェネレータはnull 70 if ($this->generator === null) { 71 return false; 72 } 73 return $this->generator->valid(); 74 } 75} 76 77// 1から指定された数までカウントアップするジェネレータ関数 78function createCountGenerator(int $max): Generator 79{ 80 echo "--- A new generator instance has been created. ---\n"; 81 for ($i = 1; $i <= $max; $i++) { 82 yield $i; 83 } 84} 85 86// ジェネレータを生成するファクトリ(クロージャ)を用意 87$generatorFactory = fn() => createCountGenerator(3); 88 89// ファクトリを渡し、巻き戻し可能なイテレータのインスタンスを作成 90$rewindable = new RewindableGenerator($generatorFactory); 91 92// 作成した巻き戻し可能なイテレータを IteratorIterator でラップする 93$iterator = new IteratorIterator($rewindable); 94 95echo "1st iteration:\n"; 96foreach ($iterator as $value) { 97 echo $value . "\n"; 98} 99 100echo "\nCalling iterator->rewind()...\n"; 101// IteratorIterator::rewind() を呼び出すと、内部の RewindableGenerator::rewind() が実行され、 102// 新しいジェネレータインスタンスが生成されることで「巻き戻し」が実現されます。 103$iterator->rewind(); 104 105echo "\n2nd iteration:\n"; 106// 巻き戻されたため、再度最初からイテレーションが可能 107foreach ($iterator as $value) { 108 echo $value . "\n"; 109}
IteratorIteratorクラスのrewindメソッドは、内包するイテレータを先頭の位置に巻き戻すために使用されます。このメソッドは引数を取らず、戻り値もありません(void)。foreachループが開始される際に自動的に呼び出されるほか、明示的に呼び出すことも可能です。
PHPのジェネレータは、メモリを節約できる便利な機能ですが、通常は一度しか繰り返し処理ができません。一度最後まで処理すると、巻き戻して再利用することは不可能です。
このサンプルコードは、その制限を回避する実践的な例を示しています。Iteratorインターフェースを実装したカスタムクラスRewindableGeneratorを作成し、そのrewindメソッド内で新しいジェネレータを再生成する処理を定義しています。
IteratorIteratorでこのカスタムクラスのインスタンスを内包し、$iterator->rewind()を呼び出すと、内部のRewindableGeneratorのrewindメソッドが実行されます。その結果、新しいジェネレータが生成され、再び最初から繰り返し処理ができるようになります。これにより、本来は一度しか使えないジェネレータを、擬似的に「巻き戻し」て何度も利用することが可能になります。
このコードは、通常一度しかループできないPHPのジェネレータを、複数回ループ可能にするための実装例です。rewind()メソッドが呼ばれるたびに、ジェネレータを生成する関数を再実行し、全く新しいジェネレータインスタンスを作り直すことで、見かけ上の「巻き戻し」を実現しています。注意点として、rewind()のたびにジェネレータの生成処理が毎回実行されるため、その処理に時間がかかる場合はパフォーマンスに影響する可能性があります。foreachループは開始時に自動でrewind()を呼び出します。IteratorIteratorは、内部に持つRewindableGeneratorオブジェクトのrewind()メソッドを呼び出す役割を担っています。
IteratorIterator::rewind() で先頭に戻る
1<?php 2 3declare(strict_types=1); 4 5/** 6 * IteratorIterator::rewind() の動作を示すサンプルコード 7 * 8 * この関数は、IteratorIteratorがラップしているイテレータを 9 * 先頭に巻き戻す方法を示します。 10 */ 11function demonstrateRewind(): void 12{ 13 // 1. 元となるデータと、それを扱うためのイテレータを準備します。 14 $fruits = ['Apple', 'Banana', 'Cherry']; 15 $arrayIterator = new ArrayIterator($fruits); 16 17 // 2. ArrayIteratorをIteratorIteratorでラップします。 18 $iterator = new IteratorIterator($arrayIterator); 19 20 // 3. 最初のループ:foreachは内部で自動的にrewind()を呼び出してから開始します。 21 echo "1回目のループ:\n"; 22 foreach ($iterator as $key => $value) { 23 echo "{$key}: {$value}\n"; 24 } 25 26 // ループが終了すると、イテレータの内部ポインタは末尾を指しています。 27 // このままでは、もう一度ループしても要素は取得できません。 28 29 echo "\n"; 30 echo "イテレータを rewind() で先頭に巻き戻します。\n"; 31 32 // 4. rewind()メソッドを明示的に呼び出し、イテレータのポインタを先頭に戻します。 33 $iterator->rewind(); 34 35 // 5. 2回目のループ:rewind()したので、再び先頭から要素をたどることができます。 36 // ここではイテレータの仕組みを分かりやすくするため、whileループで手動で操作します。 37 echo "2回目のループ:\n"; 38 while ($iterator->valid()) { // valid(): 現在位置が有効かチェック 39 $key = $iterator->key(); // key(): 現在のキーを取得 40 $value = $iterator->current(); // current(): 現在の値を取得 41 echo "{$key}: {$value}\n"; 42 $iterator->next(); // next(): ポインタを次に進める 43 } 44} 45 46demonstrateRewind();
IteratorIterator::rewind()は、ラップしているイテレータをその先頭に巻き戻すためのメソッドです。一度、ループ処理などで最後まで進んでしまったイテレータを、もう一度最初から使えるようにリセットする役割を持ちます。このメソッドは引数を取らず、何か値を返すこともありません(戻り値はvoid)。その目的は、イテレータオブジェクトの内部的なポインタ(現在位置)を先頭に戻すという操作自体にあります。
サンプルコードでは、まず配列から作成したArrayIteratorをIteratorIteratorでラップしています。1回目のforeachループでは、PHPがループ開始時に自動的にrewind()を呼び出すため、すべての要素が問題なく表示されます。ループ終了後、イテレータは末尾を指したままです。そこで、$iterator->rewind()を明示的に呼び出すことで、ポインタを強制的に先頭に戻しています。これにより、2回目のwhileループでも、再び先頭からすべての要素をたどることが可能になります。このように、イテレータを複数回にわたって先頭から処理したい場合にrewind()は不可欠です。
IteratorIterator::rewind()は、イテレータを先頭に巻き戻し、再度初めから要素をたどれるようにするメソッドです。PHPのforeachループは、処理開始時に内部で自動的にrewind()を呼び出すため、通常は開発者がこのメソッドを直接使う必要はありません。しかし、一度ループを終えたイテレータを、while文などを使って手動で再度先頭から処理したい場合に明示的な呼び出しが必要になります。注意点として、このメソッドが機能するかは、ラップしている元のイテレータが巻き戻しをサポートしているかに依存します。例えば、ジェネレータのように一度しか走査できないイテレータでは、rewind()を呼び出すとエラーになるため注意が必要です。