ジェネレータ

PHP5.5からの機能。

  • 値を順番に生成して返すオブジェクト
  • 関数のように記述して定義するが、値を返す(中断する)命令はreturnでなくyield
  • 内部でどこまで処理したか覚えているので、呼ぶたびにその途中から処理される
  • 配列+foreachと比較してのメリット
    • 10万個の要素をforeachで回す時、一旦リストにしてしまうと10万個分のメモリが必要になるが、ジェネレータなら呼ばれるたびに生成するので省メモリ
    • 要素の生成に時間がかかる場合(ネットワーク越しに入力を待つ、ファイルを1行ずつ処理するなど)、リストにしてからforeachすると全て生成するまでループが開始されないが、ジェネレータなら1行読み込むたびに処理できる(ストリーム処理に便利)
  • 先頭からのみ処理できる。リストのように途中の要素にアクセスはできない

基本的にはforeachで回す。

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を使わず任意のタイミングで、でも順番に次の値を取得したい場合があったので、やり方をメモ。大まかには、ジェネレータオブジェクトを取得し、用意された関数を呼ぶとよい。


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までが一度に処理されてしまう。

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()でできる。

programming/php/tips/generator.txt · 最終更新: 2016/08/01 by ikatakos
CC Attribution 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0