**PHP5.5.0** バージョンでは、単純な反復子を作成するための反復子インターフェイス *(Iterator)* の実装の複雑さを簡素化するために、ジェネレーター *(Generators)* 機能が追加されました。
ジェネレーターを使用すると、反復するオブジェクトを事前にメモリ内に構築する必要がなく、foreach を使用して一連のデータを反復することが簡単にできるため、メモリのオーバーヘッドが大幅に削減されます。
ジェネレーター関数が呼び出されると、オブジェクトを反復処理するときに、PHP は必要に応じてジェネレーター関数を呼び出し、ジェネレーターで新しいキーを使用します。yield という単語が新しい値を生成すると、保存されます。イテレータの内部状態。イテレータに生成する新しい値がない場合、ジェネレータ関数は直接終了でき、外部関数は実行を継続します。
ジェネレーター関数では、return ステートメントを使用して値を返すことはできません。値を返すために return を使用すると、コンパイラ エラーが生成されることに注意してください。ただし、イテレータが終了する空の return を使用することは問題ありません。
ジェネレーター関数は通常の関数と同じですが、唯一の違いは、関数内で yield キーワードが使用されることです。 yield ステートメントはジェネレーター関数の核心であると言えます。yield ステートメントは return ステートメントと同じですが、return ステートメントを使用して return を実行した後に関数が終了するという点が異なります。関数は単に一時停止され、外部関数に転送され、次回ジェネレーター関数が呼び出されたときに、ジェネレーター関数内のコードの実行が続行されます。
簡単な例では、foreach 反復関数 range の戻り値を使用します。 range(0, 1000000) を呼び出すと、100M を超えるメモリが消費されます。ジェネレーターを使用する場合、消費するメモリは 1KB 未満にとどまります。
<?phpfunction xrange($start, $end) { if ($start > $end) { throw new RuntimeException("起始值不能大于截止值"); } for ($i = $start; $i <= $end; $i += 1) { // 使用yield关键字,每次到这里函数都会返回$i的值,并且控制权交给外部函数继续执行 yield $i; }}foreach (xrange(1, 9) as $number) { echo "$number ";}
上の例の出力は次のとおりです:
上の例では、関数内で Yield という名前の関数を作成して戻り値を継続的に生成し、xrange(1, 9) を呼び出すと、ジェネレーターオブジェクトを作成します。 xrange オブジェクトを出力するように foreach 行を変更すると、
...$xrange_res = xrange(1, 9);var_dump($xrange_res);foreach( $xrange_res as $number){...
Output
xrange(1, 9) が実行されると、実際に Generator オブジェクトが返されることがわかります。
上記の例では、yield ステートメントを使用すると、別のステートメント行として実行されます。つまり、yield ステートメントは結果を外部に生成します。反復プロセスから結果を取得する方法 ジェネレーター関数の外で値を取得する場合はどうすればよいでしょうか?
ジェネレーター関数を呼び出した後、Generator オブジェクトが返されるため、send メソッドを呼び出した後、オブジェクトの send メソッドを呼び出して外部からジェネレーター関数に値を渡すことができるため、常に方法があります。 send 関数に送信された値を収集します。
<?phpfunction gen() { $ret = (yield 'yield1'); var_dump("-->" . $ret); $ret = (yield 'yield2'); var_dump("-->" . $ret);}$gen = gen();var_dump($gen->current());var_dump($gen->send('ret1'));var_dump($gen->send('ret2'));
出力:
ここでは、まず gen という名前のジェネレーター オブジェクトを作成し、次に $gen->current() メソッドの戻り値を出力します。これはイテレーターの最初の反復中に生成されます。なので、yield1 が出力されます。
次に $gen->send('ret') メソッドを呼び出します。このとき、ジェネレーターの最初の yield ステートメントはメソッドによって渡された値 ret1 を返すため、$ret の値は ret1 として出力されます。
その後、ジェネレーターは 3 番目のステートメント $ret = (yield 'yield2') を内部で実行するため、2 番目の外部 var_dump は yield2 を出力します。 $gen->send('ret2') への最後の呼び出しは最初と似ていますが、今回はジェネレーターが内部で yield を呼び出した後、yield がないため、NULL が返されます。
ここでの $ret = (yield 'yield2') ステートメントでは、yield 'yield2' ステートメントが括弧内に含まれていることに注意してください。これは、式コンテキストで yield を使用する場合、yield を括弧内に置く必要があります。それ以外の場合は、エラーが報告されます。
前の例では、常に 1 つの値を返すために yield キーワードを使用しました。毎回、マネージド配列を走査した場合と同じ結果を返します。
yield key => val
出力:
参照の使用
<?phpfunction gen2() { $array = [ 'username' => 'mylxsw', 'site' => 'http://aicode.cc' ]; foreach ($array as $key => $val) { yield $key => $val; }}foreach(gen2() as $key => $val) { var_dump($key . ' : ' . $val);}
上記の例では、&$number がジェネレーター関数の定義と走査で使用されていることに注意してください。
最後に、ジェネレーターはカスタム反復子オブジェクトとまったく同じではありません。ジェネレーターが反復を開始すると、巻き戻すことはできず、反復が完了するまで前方に反復することしかできません。ジェネレーター オブジェクトを複数回反復する場合は、ジェネレーター関数を複数回呼び出して新しいジェネレーター オブジェクトを作成するか、 clone キーワードを使用します。
参考:
コルーチン (PHP で!) ジェネレーターを使用した協調マルチタスク