PHP入門 ジェネレーター

最終更新日

ジェネレーター(Generator)の見た目は、普通の関数です。しかし、普通の関数がreturn命令で値を返したらそれで終わりであるのに対して、ジェネレーターはyieldという命令を利用することで、つど、その時々の値を返すせる点が異なります。

<?php
function myGen(){
yield 'あいうえお';
yield 'かきくけこ';
yield 'さしすせそ';
}

foreach(myGen() as $value){
    print $value.'<br />';
}
実行結果
実行結果

yieldは、returnとよく似た命令で、関数の値を呼び出し元に返します。しかし、return命令がその場で関数の実行を終了するのに対して、yield命令は処理を一定停止します。つまり、次に呼び出されたときには、その時点から処理を再開できます。よって、定義されたジェネレーターmyGenをforeach命令に渡すことで、ループのつど、先頭から順番にyield命令による値が返されます。

同じ関数を3回呼び出した場合、return命令とyield命令の違い
ジェネレーター関数の戻り値は、yield命令によって返されたものではなく、Generatorオブジェクトです。もしも、明示的に型宣言するならば、「function MyGen(); Generator{~}」となります。

ジェネレーターの結果を取得する

ジェネレーター関数でもreturn命令は利用できます。その場合、return命令はジェネレーターの最終的な結果を表します。return命令によってジェネレーターは終了するので、一般的にreturn命令を呼び出すのは、ジェネレーターがすべての処理を終えたタイミングです(❶)。return命令で返された値を取得するには、GeneratorオブジェクトのgetReturnメソッドを利用します(❷)。

<?php
function readLines(string $path){
    //行数
    $i = 0;
    $file = fopen($path, 'rb') or die('ファイルが見つかりません');
    //行単位にテキストを取得&yield
    while($line = fgets($file, 1024)){
        $i++;
        yield $line;
    }
    fclose($file);
    //読み込んだテキストの行数を返す
    return $i;                               //➊
}

$gen = readLines('sample.dat');
foreach($gen as $line){
    print $line.'<br />';
}
print "{$gen->getReturn()}行ありました"       //➋
実行結果
実行結果

一部の処理を他のジェネレーターに委譲する

yield from命令を利用することで、ジェネレーターの中で別のジェネレーター、または配列を呼び出し、これを列挙できます。すべての値を列挙できたら、改めて後続のyield命令を続行するのです。

構文:yield from関数

yield from list
list他のジェネレーター、配列など
yield from命令
yield from命令

例:readFiles関数は、指定されたファイル(配列)から順にテキストを取り出していくためのジェネレーターです。この際、readFiles関数は、もう1つのジェネレーターに処理を委ねます。

<?php
function readFiles(string ...$files){
    //配列から順にファイルパスを取り出す
   foreach($files as $file){
    //ジェネレーターreadLinesに処理を委ねる
    yield from readLines($file);
   }
}

function readLines(string $path){
    $file = fopen($path, 'rb') or die('ファイルが見つかりません');
    //行単にテキストを取得
    while($line = fgets($file, 1024)){
        yield $line;
    }
    fclose($file);
}
//sample.dat/sample2.datの内容を順に列挙
foreach(readFiles('sample.dat', 'sample2.dat') as $line){
    print $line.'<br />';
}