Webエンジニア向けプログラミング解説動画をYouTubeで配信中!
▶ チャンネル登録はこちら

【PHP8.x】SeekableIterator::rewind()メソッドの使い方

rewindメソッドの使い方について、初心者にもわかりやすく解説します。

作成日: 更新日:

基本的な使い方

rewindメソッドは、イテレータの内部ポインタを最初の要素にリセットするために使用されるメソッドです。PHP 8においてSeekableIteratorインターフェースの一部として定義されており、このインターフェースを実装するクラスは、コレクションやデータセットの先頭から再度要素を処理できるように、このメソッドを必ず実装する必要があります。

通常、イテレータを使用してデータ構造の要素を順に走査する場合、現在の位置を示す内部ポインタが存在します。rewindメソッドが呼び出されると、このポインタが最初の要素、またはイテレーションを開始するべき位置に戻されます。これにより、すでに一度最後まで処理したイテレータであっても、再度最初から要素を繰り返し処理することが可能になります。

例えば、PHPのforeachループを使ってイテレータを処理する際、ループの開始時に自動的にrewindメソッドが呼び出され、イテレーションがデータの最初から行われることが保証されます。開発者が明示的にこのメソッドを呼び出すことも可能で、一度イテレーションを中断し、改めて最初の要素から処理を再開したい場合に利用できます。このメソッドは、イテレータが複数回にわたって利用可能であることを保証するための重要な役割を担っています。

構文(syntax)

1$iterator->rewind();

引数(parameters)

引数なし

引数はありません

戻り値(return)

戻り値なし

戻り値はありません

サンプルコード

PHPジェネレータを巻き戻し可能にする

1<?php
2
3/**
4 * RewindableGeneratorクラスは、PHPのGeneratorを巻き戻し可能にするラッパーです。
5 * 通常のGeneratorは一度しかイテレートできませんが、
6 * このクラスはSeekableIteratorインターフェースを実装し、
7 * rewind()やseek()メソッドが呼ばれたときにGeneratorを再生成することで、
8 * 複数回のイテレーションや任意の地点へのシークを可能にします。
9 *
10 * システムエンジニアを目指す初心者の方へ:
11 * Iteratorインターフェースは、PHPでforeachループがどのように動作するかを定義する基本的なものです。
12 * SeekableIteratorは、そのIteratorに加えて、任意の場所へ「シーク(移動)」できる機能を追加します。
13 * このコードでは、ジェネレータを「工場」のように扱い、必要に応じて新しいジェネレータを再生産することで、
14 * 一度きりのジェネレータをあたかも巻き戻せるかのように見せています。
15 */
16class RewindableGenerator implements SeekableIterator
17{
18    /**
19     * ジェネレータインスタンスを生成するためのクロージャ(関数)。
20     * このクロージャは引数なしでGeneratorインスタンスを返す必要があります。
21     * @var Closure
22     */
23    private Closure $generatorFactory;
24
25    /**
26     * 現在イテレーション中のGeneratorインスタンス。
27     * @var Generator|null
28     */
29    private ?Generator $currentGenerator = null;
30
31    /**
32     * 現在のイテレーション位置(キー)。
33     * SeekableIterator::key() メソッドで返されます。
34     * @var int
35     */
36    private int $position = 0;
37
38    /**
39     * RewindableGeneratorの新しいインスタンスを生成します。
40     *
41     * @param callable $generatorFactory ジェネレータを生成するクロージャ。
42     *                                   例: `fn(): Generator => someGenerator()`
43     */
44    public function __construct(callable $generatorFactory)
45    {
46        $this->generatorFactory = Closure::fromCallable($generatorFactory);
47        $this->rewind(); // 初回起動時にジェネレータを生成し、先頭にセットします。
48    }
49
50    /**
51     * イテレータを最初の要素に巻き戻します。
52     * ここで新しいGeneratorインスタンスを生成し直すことで、
53     * 巻き戻し不可能なGeneratorを巻き戻し可能にします。
54     */
55    public function rewind(): void
56    {
57        // 新しいGeneratorインスタンスを生成し、現在のジェネレータを置き換えます。
58        $this->currentGenerator = ($this->generatorFactory)();
59        $this->position = 0; // 位置を先頭(0)にリセットします。
60    }
61
62    /**
63     * 現在の要素の値を返します。
64     * @return mixed 現在の要素の値。
65     */
66    public function current(): mixed
67    {
68        return $this->currentGenerator->current();
69    }
70
71    /**
72     * 現在の要素のキーを返します。
73     * このクラスでは、イテレーション位置(0から始まる整数)をキーとして使用します。
74     * @return int 現在の要素のキー。
75     */
76    public function key(): int
77    {
78        return $this->position;
79    }
80
81    /**
82     * イテレータを次の要素に進めます。
83     */
84    public function next(): void
85    {
86        $this->currentGenerator->next();
87        $this->position++; // 位置をインクリメントします。
88    }
89
90    /**
91     * 現在のイテレーション位置が有効かどうかをチェックします。
92     * @return bool 有効な場合はtrue、そうでない場合はfalse。
93     */
94    public function valid(): bool
95    {
96        return $this->currentGenerator->valid();
97    }
98
99    /**
100     * 指定された位置までイテレータを進めます(シークします)。
101     * このメソッドは、まずジェネレータを巻き戻し(再生成し)、
102     * その後、指定された位置までnext()を呼び出し続けます。
103     *
104     * @param int $position シークする位置(0から始まるインデックス)。
105     * @throws OutOfBoundsException 指定された位置が負の値であるか、イテレータの範囲を超えている場合。
106     */
107    public function seek(int $position): void
108    {
109        if ($position < 0) {
110            throw new OutOfBoundsException("Cannot seek to a negative position: {$position}.");
111        }
112
113        // まずイテレータを先頭に巻き戻します。
114        $this->rewind();
115
116        // 指定された位置までイテレータを進めます。
117        while ($this->position < $position && $this->valid()) {
118            $this->next();
119        }
120
121        // 指定位置まで到達できなかった場合(イテレータが終了した場合)は例外を投げます。
122        if ($this->position !== $position) {
123            throw new OutOfBoundsException("Cannot seek to position {$position}: iterator ended prematurely (only {$this->position} elements were available).");
124        }
125    }
126}
127
128// --- サンプルコードの使用例 ---
129
130// ジェネレータ関数を定義します。これは一度しかイテレートできません。
131function simpleNumberSequence(int $start, int $end): Generator
132{
133    echo "[Generator: Creating a sequence from $start to $end]" . PHP_EOL;
134    for ($i = $start; $i <= $end; $i++) {
135        yield $i;
136    }
137    echo "[Generator: Sequence finished]" . PHP_EOL;
138}
139
140// RewindableGeneratorを使って、巻き戻し可能な数値シーケンスジェネレータを作成します。
141// fn() => ... はPHP 7.4以降のアロー関数構文で、短いクロージャを記述するのに便利です。
142$rewindableNumbers = new RewindableGenerator(fn(): Generator => simpleNumberSequence(1, 3)); // 1, 2, 3 を生成
143
144echo "--- 最初のイテレーション (foreachがrewind()を自動で呼び出します) ---" . PHP_EOL;
145foreach ($rewindableNumbers as $key => $value) {
146    echo "  Key: $key, Value: $value" . PHP_EOL;
147}
148
149echo PHP_EOL . "--- 2回目のイテレーション (再度foreachがrewind()を呼び出します) ---" . PHP_EOL;
150foreach ($rewindableNumbers as $key => $value) {
151    echo "  Key: $key, Value: $value" . PHP_EOL;
152}
153
154echo PHP_EOL . "--- rewind() メソッドの明示的な使用例 ---" . PHP_EOL;
155$rewindableNumbers->rewind(); // 明示的にイテレータを先頭に巻き戻します
156echo "  Manually rewound. Starting iteration from the beginning." . PHP_EOL;
157while ($rewindableNumbers->valid()) {
158    echo "  Key: " . $rewindableNumbers->key() . ", Value: " . $rewindableNumbers->current() . PHP_EOL;
159    $rewindableNumbers->next();
160}
161
162echo PHP_EOL . "--- seek() メソッドの使用例: 位置 1 (2番目の要素) から開始 ---" . PHP_EOL;
163try {
164    $rewindableNumbers->seek(1); // 位置 1 (0始まりなので2番目の要素) にシークします
165    echo "  Seeked to position 1. Starting iteration from here." . PHP_EOL;
166    while ($rewindableNumbers->valid()) {
167        echo "  Key: " . $rewindableNumbers->key() . ", Value: " . $rewindableNumbers->current() . PHP_EOL;
168        $rewindableNumbers->next();
169    }
170} catch (OutOfBoundsException $e) {
171    echo "  エラー: " . $e->getMessage() . PHP_EOL;
172}
173
174echo PHP_EOL . "--- seek() メソッドの使用例: 存在しない位置へのシーク ---" . PHP_EOL;
175try {
176    $rewindableNumbers->seek(10); // 範囲外の位置にシークを試みます
177} catch (OutOfBoundsException $e) {
178    echo "  エラー: " . $e->getMessage() . PHP_EOL;
179}

PHPのSeekableIteratorインターフェースに含まれるrewindメソッドは、イテレータをデータセットの最初の要素に巻き戻すための機能を提供します。このメソッドは引数を取らず、戻り値もありません(void)。

提供されたサンプルコードのRewindableGeneratorクラスは、PHPの通常のジェネレータが一度しかイテレートできないという制約に対し、このrewindメソッドを活用して複数回のイテレーションを可能にしています。具体的には、rewind()が呼び出されるたびに、ジェネレータを生成するための「工場」のような役割を果たすgeneratorFactoryを使って新しいジェネレータインスタンスを生成し直します。これにより、イテレーションの内部状態(現在位置を示すposition)も0にリセットされ、データ処理を常に最初から開始できるようになります。

foreachループでRewindableGeneratorのインスタンスを使用する際にも、ループが開始されるたびにこのrewind()メソッドが自動的に呼び出され、毎回データセットの先頭から処理が実行されます。このように、rewindメソッドは、一度しか利用できないジェネレータのようなデータソースを、繰り返し利用可能な形に変えるための重要な仕組みです。

このサンプルコードでは、一度しかイテレートできないPHPのGeneratorを複数回利用可能にするRewindableGeneratorクラスと、SeekableIteratorインターフェースのrewindメソッドについて説明しています。 重要な注意点は、rewind()メソッドやseek()メソッドが呼ばれるたびに、内部で新しいGeneratorが生成され直している点です。これは、実際のGeneratorが巻き戻るのではなく、常に最初から新しいGeneratorを作り直して代替していることを意味します。 そのため、Generatorの生成処理にコストがかかる場合、頻繁なrewind()やseek()の呼び出しはパフォーマンスに影響を与える可能性があります。 また、foreachループは通常、開始時に自動的にrewind()を呼び出すため、明示的な呼び出しは不要なケースが多いですが、複数回のイテレーションにはこの機能が不可欠です。 この仕組みを理解することが、Generatorを安全かつ効率的に扱う上で重要です。

PHP rewind arrayでイテレータを巻き戻す

1<?php
2
3/**
4 * ArrayIterator は SeekableIterator インターフェースを実装しているため、
5 * rewind() メソッドを使用してイテレータを巻き戻すことができます。
6 * このサンプルは、配列のイテレータを途中で停止し、rewind() を使って
7 * 先頭に戻してから再度イテレーションを開始する方法を示します。
8 */
9
10// サンプル用の配列を定義します。
11$data = ['apple', 'banana', 'cherry', 'date', 'elderberry'];
12
13// ArrayIterator を作成し、配列をイテレートできるようにします。
14$arrayIterator = new ArrayIterator($data);
15
16echo "--- 最初のイテレーション (途中で停止) ---" . PHP_EOL;
17$count = 0;
18foreach ($arrayIterator as $key => $value) {
19    echo "現在値: " . $value . PHP_EOL;
20    $count++;
21    // 最初の2つの要素を処理した後、イテレーションを中断します。
22    if ($count >= 2) {
23        break;
24    }
25}
26
27echo PHP_EOL;
28echo "--- rewind() を呼び出し、イテレータを先頭に巻き戻します ---" . PHP_EOL;
29// rewind() メソッドを呼び出すことで、イテレータの内部ポインタが配列の最初の要素に戻ります。
30$arrayIterator->rewind();
31
32echo PHP_EOL;
33echo "--- rewind() 後のイテレーション (先頭から再開) ---" . PHP_EOL;
34// rewind() 後、再度イテレーションを開始すると、最初の要素から処理が再開されます。
35foreach ($arrayIterator as $key => $value) {
36    echo "巻き戻し後の現在値: " . $value . PHP_EOL;
37}
38
39?>

PHP 8のSeekableIteratorインターフェースが提供するrewind()メソッドは、イテレータの内部ポインタ(現在位置)をデータの先頭に戻す機能を提供します。このメソッドは処理に必要な引数を取らず、戻り値もありません。イテレータは、配列のような複数の要素を持つコレクションを一つずつ順番に処理するための仕組みです。

提供されたサンプルコードでは、配列をイテレートするためにArrayIteratorクラスを使用しています。ArrayIteratorSeekableIteratorインターフェースを実装しているため、このrewind()メソッドが利用可能です。まず、配列の要素をいくつか処理し、イテレーションを途中で中断します。その後、$arrayIterator->rewind();を呼び出すと、イテレータは自身の内部状態をリセットし、最初の要素を指すようになります。これにより、再度foreachループなどでイテレーションを開始すると、途中で中断した場所からではなく、必ず最初の要素から処理が再開されます。

このrewind()メソッドは、一度イテレーションを行った、または途中で停止したイテレータを、最初から再度利用したい場合に大変便利です。これにより、同じデータを複数回、異なる目的で順に処理するような場面で、効率的なコードを記述できます。

このrewind()メソッドは、イテレータの内部ポインタをデータの先頭に戻すために使用します。SeekableIteratorインターフェースを実装しているイテレータにのみ利用可能であり、すべてのイテレータがこの機能を持つわけではない点に注意が必要です。rewind()は戻り値がないため、呼び出し結果を変数に代入することはできません。通常のforeachループは開始時に自動でrewind()を呼び出してイテレーションを開始するため、サンプルコードのように途中でイテレーションを中断し、改めて先頭から処理を再開したい場合に手動で呼び出すことが主な用途となります。配列やコレクションそのものを初期化するのではなく、あくまでイテレーションの開始位置をリセットする機能であることを理解してください。

関連コンテンツ