ジェネレータ
PHP5.5からの機能。
- 値を順番に生成して返すオブジェクト
- 関数のように記述して定義するが、値を返す(中断する)命令はreturnでなくyield
- 内部でどこまで処理したか覚えているので、呼ぶたびにその途中から処理される
- 配列+foreachと比較してのメリット
- 10万個の要素をforeachで回す時、一旦リストにしてしまうと10万個分のメモリが必要になるが、ジェネレータなら呼ばれるたびに生成するので省メモリ
- 要素の生成に時間がかかる場合(ネットワーク越しに入力を待つ、ファイルを1行ずつ処理するなど)、リストにしてからforeachすると全て生成するまでループが開始されないが、ジェネレータなら1行読み込むたびに処理できる(ストリーム処理に便利)
- 先頭からのみ処理できる。リストのように途中の要素にアクセスはできない
基本的にはforeachで回す。
1 2 3 4 5 6 7 8 9 10 |
function rangeGenerator( $start , $limit ) { for ( $i = $start ; $i < $limit ; $i ++) { yield $i ; } } foreach (rangeGenerator(1, 9) as $number ) { echo "$number " ; } // 1 2 3 4 5 6 7 8 |
ただ今回、foreachを使わず任意のタイミングで、でも順番に次の値を取得したい場合があったので、やり方をメモ。大まかには、ジェネレータオブジェクトを取得し、用意された関数を呼ぶとよい。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
echo '<pre>' ; function generatorTest() { echo 'GEN_START' . PHP_EOL; yield 0; foreach (range(1, 10) as $i ) { echo $i . PHP_EOL; yield $i + 100; } } $gen = generatorTest(); // (ジェネレータオブジェクトを返すだけで、何も出力しない) echo 'START' . PHP_EOL; // START echo $gen ->current() . PHP_EOL; // GEN_START (ジェネレータ内部のecho) // 0 (ジェネレータから0が返される) $gen ->next(); // 1 (ジェネレータ内部のecho) echo 'B' . PHP_EOL; // B echo $gen ->current() . PHP_EOL; // 101 (101が返される) echo $gen ->current() . PHP_EOL; // 101 (何度でも101が返される) $gen ->next(); // 2 (ジェネレータ内部のecho) $gen ->next(); // 3 (ジェネレータ内部のecho) echo '</pre>' ; |
next()は次のyieldまで進めるだけで、値は返さない。
最初にいきなりnext()を呼ぶと、2つ目のyieldまでが一度に処理されてしまう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
echo '<pre>' ; function generatorTest2() { foreach (range(1, 10) as $i ) { echo $i . PHP_EOL; yield $i + 100; } } $gen = generatorTest2(); // $gen ->next(); // 1 (ジェネレータ内部のecho) // 2 (ジェネレータ内部のecho) echo $gen ->current() . PHP_EOL; // 102 echo '</pre>' ; |
つまりジェネレータのyieldを順番に返したいだけなら、まずcurrent()を呼んでから、次のためにnext()を呼ぶ、ということになる。
要素が残っているかのチェックは、valid()でできる。