ホームページ >バックエンド開発 >PHPチュートリアル >PHP 学習 --generatorsGenerators,--generatorsgenerators_PHP チュートリアル
(PHP 5 >= 5.5.0, PHP 7)
ジェネレーターは、単純なオブジェクトの反復を実装する簡単な方法を提供します。Iterator インターフェイスを実装するクラスを定義する場合と比較して、パフォーマンスのオーバーヘッドと複雑さが大幅に軽減されます。
ジェネレーターを使用すると、メモリ内に配列を作成せずに foreach ブロックにコードを記述して、一連のデータを反復処理できます。これにより、メモリの制限に達したり、かなりの処理時間がかかってしまいます。代わりに、通常のカスタム関数と同じようにジェネレーター関数を作成できます。通常の関数が 1 回だけ返すのではなく、ジェネレーターは反復する必要がある値を生成するために必要なだけ何度でも生成できます。
簡単な例は、ジェネレーターを使用して range() 関数を再実装することです。 標準の range() 関数は、範囲内のすべての値を含む配列をメモリ内に生成し、その配列を返す必要があるため、複数の大きな配列が生成されます。 たとえば、range(0, 1000000) を呼び出すと、メモリ使用量が 100 MB を超えます。
代わりに、Iterator オブジェクトを作成し、内部でジェネレーターの現在の状態を追跡するのに十分なメモリのみを必要とする xrange() ジェネレーターを実装することもできます。したがって、必要なメモリは 1K バイト未満です。
例 #1 range() をジェネレーターとして実装する
リーリー上記のルーチンは次のように出力します:
リーリージェネレーター関数が初めて呼び出されるとき、内部 Generator クラスのオブジェクトが返されます。このオブジェクトは、前方専用イテレーター オブジェクトとほぼ同じ方法で Iterator インターフェースを実装し、以下のメソッドを提供します。ジェネレーターへの値の送信とジェネレーターからの値の戻りを含む、ジェネレーターの状態を操作するために呼び出されます。
ジェネレーター関数は通常の関数と似ていますが、通常の関数は値を返すのに対し、ジェネレーターは必要なだけの値を生成できる点が異なります。
ジェネレーターが呼び出されると、オブジェクトを反復処理できるオブジェクトが返されます (foreach ループなどを通じて)。PHP は値が必要になるたびにジェネレーター関数を呼び出し、ジェネレーターの状態を保存します。値を生成した後、次の値を生成する必要があるときに呼び出し元の状態に復元できるようにします。
それ以上値を生成する必要がなくなったら、ジェネレーター関数は単に終了することができ、ジェネレーターを呼び出したコードは、配列が反復処理されたかのように実行を継続できます。
注:
ジェネレーターは値を返すことができません。値を返すとコンパイル エラーが発生します。ただし、return null は有効な構文であり、ジェネレーターを終了して実行を継続します。
収益キーワード
generator 関数の中核は yield キーワードです。最も単純な形式では return ステートメントのように見えますが、通常の return は値を返して関数の実行を終了するのに対し、yield はジェネレーターをループして単に実行を一時停止するコードに値を返すという点が異なります。ジェネレーター機能。
例 #1 値を生成する簡単な例
リーリー上記のルーチンは次のように出力します:
リーリー注:
内部的には、生成された値は、非連想配列と同様に、連続する整数インデックスとペアになります。
式のコンテキスト (代入式の右側など) で yield を使用する場合は、括弧を使用して yield 宣言を囲む必要があります。 たとえば、これは有効です:
リーリーこれは違法であり、PHP5 ではコンパイル エラーが生成されます:
リーリー括弧内の制限は PHP 7 には適用されません。
この構文は、ジェネレーター オブジェクトの Generator::send() メソッドと組み合わせて使用できます。
値を生成するキー名を指定します
PHP の配列は、関連付けられたキーと値のペアの配列をサポートしており、ジェネレーターもそれをサポートしています。したがって、単純な値を生成するだけでなく、値を生成するときにキー名を指定することもできます。
以下に示すように、キーと値のペアの生成は、連想配列の定義と非常に似ています。
例 #2 キーと値のペアを生成する
<?<span>php </span><span>/*</span><span> * 下面每一行是用分号分割的字段组合,第一个字段将被用作键名。 </span><span>*/</span> <span>$input</span> = <<<'EOF' 1<span>;PHP;Likes dollar signs </span>2<span>;Python;Likes whitespace </span>3<span>;Ruby;Likes blocks EOF; </span><span>function</span> input_parser(<span>$input</span><span>) { </span><span>foreach</span> (<span>explode</span>("\n", <span>$input</span>) <span>as</span> <span>$line</span><span>) { </span><span>$fields</span> = <span>explode</span>(';', <span>$line</span><span>); </span><span>$id</span> = <span>array_shift</span>(<span>$fields</span><span>); yield </span><span>$id</span> => <span>$fields</span><span>; } } </span><span>foreach</span> (input_parser(<span>$input</span>) <span>as</span> <span>$id</span> => <span>$fields</span><span>) { </span><span>echo</span> "<span>$id</span>:\n"<span>; </span><span>echo</span> " <span>$fields</span>[0]\n"<span>; </span><span>echo</span> " <span>$fields</span>[1]\n"<span>; } </span>?>以上例程会输出:
<span>1</span><span>: PHP Likes dollar signs </span><span>2</span><span>: Python Likes whitespace </span><span>3</span><span>: Ruby Likes blocks</span>和之前生成简单值类型一样,在一个表达式上下文中生成键值对也需要使用圆括号进行包围:
<span>$data</span> = (yield <span>$key</span> => <span>$value</span>);生成null值
Yield可以在没有参数传入的情况下被调用来生成一个
NULL
值并配对一个自动的键名。Example #3 生成
NULL
s<?<span>php </span><span>function</span><span> gen_three_nulls() { </span><span>foreach</span> (<span>range</span>(1, 3) <span>as</span> <span>$i</span><span>) { yield; } } </span><span>var_dump</span><span>(iterator_to_array(gen_three_nulls())); </span>?>以上例程会输出:
array(<span>3</span><span>) { [</span><span>0</span>]=><span> NULL [</span><span>1</span>]=><span> NULL [</span><span>2</span>]=><span> NULL }</span>使用引用来生成值
生成函数可以像使用值一样来使用引用生成。这个和returning references from functions(从函数返回一个引用)一样:通过在函数名前面加一个引用符号。
Example #4 使用引用来生成值
<?<span>php </span><span>function</span> &<span>gen_reference() { </span><span>$value</span> = 3<span>; </span><span>while</span> (<span>$value</span> > 0<span>) { yield </span><span>$value</span><span>; } } </span><span>/*</span><span> * 我们可以在循环中修改$number的值,而生成器是使用的引用值来生成,所以gen_reference()内部的$value值也会跟着变化。 </span><span>*/</span> <span>foreach</span> (gen_reference() <span>as</span> &<span>$number</span><span>) { </span><span>echo</span> (--<span>$number</span>).'... '<span>; } </span>?>以上例程会输出:
<span>2</span>... <span>1</span>... <span>0</span>...Generator delegation via yield from ¶
In PHP 7, generator delegation allows you to yield values from another generator, Traversable object, or array by using the yield from keyword. The outer generator will then yield all values from the inner generator, object, or array until that is no longer valid, after which execution will continue in the outer generator.
If a generator is used with yield from, the yield from expression will also return any value returned by the inner generator.
Example #5 Basic use of yield from
<?<span>php </span><span>function</span><span> count_to_ten() { yield </span>1<span>; yield </span>2<span>; yield from [</span>3, 4<span>]; yield from </span><span>new</span> ArrayIterator([5, 6<span>]); yield from seven_eight(); yield </span>9<span>; yield </span>10<span>; } </span><span>function</span><span> seven_eight() { yield </span>7<span>; yield from eight(); } </span><span>function</span><span> eight() { yield </span>8<span>; } </span><span>foreach</span> (count_to_ten() <span>as</span> <span>$num</span><span>) { </span><span>echo</span> "<span>$num</span> "<span>; } </span>?>以上例程会输出:
<span>1</span> <span>2</span> <span>3</span> <span>4</span> <span>5</span> <span>6</span> <span>7</span> <span>8</span> <span>9</span> <span>10</span>Example #6 yield from and return values
<?<span>php </span><span>function</span><span> count_to_ten() { yield </span>1<span>; yield </span>2<span>; yield from [</span>3, 4<span>]; yield from </span><span>new</span> ArrayIterator([5, 6<span>]); yield from seven_eight(); </span><span>return</span><span> yield from nine_ten(); } </span><span>function</span><span> seven_eight() { yield </span>7<span>; yield from eight(); } </span><span>function</span><span> eight() { yield </span>8<span>; } </span><span>function</span><span> nine_ten() { yield </span>9<span>; </span><span>return</span> 10<span>; } </span><span>$gen</span> =<span> count_to_ten(); </span><span>foreach</span> (<span>$gen</span> <span>as</span> <span>$num</span><span>) { </span><span>echo</span> "<span>$num</span> "<span>; } </span><span>echo</span> <span>$gen</span>-><span>getReturn(); </span>?>以上例程会输出:
<span>1</span> <span>2</span> <span>3</span> <span>4</span> <span>5</span> <span>6</span> <span>7</span> <span>8</span> <span>9</span> <span>10</span>Comparing generators with Iterator objects
The primary advantage of generators is their simplicity. Much less boilerplate code has to be written compared to implementing an Iterator class, and the code is generally much more readable. For example, the following function and class are equivalent:
<?<span>php </span><span>function</span> getLinesFromFile(<span>$fileName</span><span>) { </span><span>if</span> (!<span>$fileHandle</span> = <span>fopen</span>(<span>$fileName</span>, 'r'<span>)) { </span><span>return</span><span>; } </span><span>while</span> (<span>false</span> !== <span>$line</span> = <span>fgets</span>(<span>$fileHandle</span><span>)) { yield </span><span>$line</span><span>; } </span><span>fclose</span>(<span>$fileHandle</span><span>); } </span><span>//</span><span> versus...</span> <span>class</span> LineIterator <span>implements</span><span> Iterator { </span><span>protected</span> <span>$fileHandle</span><span>; </span><span>protected</span> <span>$line</span><span>; </span><span>protected</span> <span>$i</span><span>; </span><span>public</span> <span>function</span> __construct(<span>$fileName</span><span>) { </span><span>if</span> (!<span>$this</span>->fileHandle = <span>fopen</span>(<span>$fileName</span>, 'r'<span>)) { </span><span>throw</span> <span>new</span> RuntimeException('Couldn\'t open file "' . <span>$fileName</span> . '"'<span>); } } </span><span>public</span> <span>function</span> <span>rewind</span><span>() { </span><span>fseek</span>(<span>$this</span>->fileHandle, 0<span>); </span><span>$this</span>->line = <span>fgets</span>(<span>$this</span>-><span>fileHandle); </span><span>$this</span>->i = 0<span>; } </span><span>public</span> <span>function</span><span> valid() { </span><span>return</span> <span>false</span> !== <span>$this</span>-><span>line; } </span><span>public</span> <span>function</span> <span>current</span><span>() { </span><span>return</span> <span>$this</span>-><span>line; } </span><span>public</span> <span>function</span> <span>key</span><span>() { </span><span>return</span> <span>$this</span>-><span>i; } </span><span>public</span> <span>function</span> <span>next</span><span>() { </span><span>if</span> (<span>false</span> !== <span>$this</span>-><span>line) { </span><span>$this</span>->line = <span>fgets</span>(<span>$this</span>-><span>fileHandle); </span><span>$this</span>->i++<span>; } } </span><span>public</span> <span>function</span><span> __destruct() { </span><span>fclose</span>(<span>$this</span>-><span>fileHandle); } } </span>?>This flexibility does come at a cost, however: generators are forward-only iterators, and cannot be rewound once iteration has started. This also means that the same generator can't be iterated over multiple times: the generator will need to either be rebuilt by calling the generator function again, or cloned via the clone keyword.
摘自:http://php.net/manual/zh/language.generators.php