この記事では、foreach # を使用しないジェネレーターでの PHP yield の使用法について説明したいと思います。 ##、
for、
while のようなループ。
yield を使用して関数をジェネレーターに変換する方法について説明します。
yield
機能に関しては、
PHP5の開発時に議題となり、
PHP5.5バージョンが正式にリリースされました。追加した。
yield の使用に関して、ほとんどの記事が
foreach# で使い果たすために yield
を使用する方法に行き詰まっていることがわかります。 ## データさん、今日はジェネレーターの構文をすべて説明したいと思います。
PHP プログラミングの入門から熟練度まで
ブロックにコードを記述して、メモリ内に配列を作成せずにデータ セットを反復処理できるようにします。これにより、メモリの制限に達したり、かなりの処理時間がかかったりする可能性があります。代わりに、通常のカスタム関数と同じように、ジェネレーター関数を作成できます。一度だけ返す通常の関数とは異なり、ジェネレーターは、必要なだけ何度でも yield
を実行して、値が必要な反復を生成できます。 公式 Web サイトを見て、php.net ジェネレーターの構文を説明しました。私はすべての単語を知っていましたが、それでもその意味を理解しているようでした。公式 Web サイトでは、主に
の構文の 2 つの部分について説明します。
<?phpfunction func(){ $data = (yield [$express]);}
構文は次のようになります。皆さんはまだ少し混乱していると思います。公式ウェブサイトにある以下のコード例を見てください。中を見てみましょう。例は混合されています。
yield の括弧の層に注目 php5.5 だと右側の $express の優先度が判断され、$data の代入文よりも低くなる可能性があります左に。そこでphp5ではyieldを使いますが、yieldの右側は演算可能な式で、左側は戻り値を受け入れて値を代入する必要があるため、この括弧が必要です。 php7ではこの問題は発生しません。#例を通して学びましょう
人間の言葉で説明するとわかりにくいので、何ができて何ができないのか、例を挙げて説明しましょう。 関連コードを gitee に載せておきますので、ローカル環境にコピーして実際に実行して体験していただくと、以下の内容が理解できると思います。git clone gitee.com/xupaul/PHP-generator-yie...
ジェネレーターの生成方法
テスト コード /php-yield-test/whatIsGenerator.php。どの関数がジェネレーターを形成できるか、どの関数が形成できないかを確認するために使用されます。実行結果は次のとおりです
関数には yield
キーワードが含まれている必要があります。関数は完全な再生関数またはクラス メソッドにすることができます。 。yield
が確実に実行されない場合でも、ジェネレーターは生成されます。参照: yield_func4yield
キーワードが機能します (送信や外部入力の処理はありません)。参照: yield_func2
eval 関数で
はい、関数内に foreach、while、または for ステートメントがあるかどうかは関係ありません。重要なのは yield です。Generator オブジェクトはジェネレーターから返されます。
##ジェネレーター関数
摘自 php.net generator
看着以上方法,是不想起了
Iterator
, 他们的确很像。同时注意,官网zh语言版本的文档没有索引方法getReturn
,访问也是404。文档以en版为准,ch做参考。
以上就是生成器所有的方法,我们一个个来看。
测试方法代码 /php-yield-test/generatorMothod.php, 这里面对每个方法都有使用举例,运行结果如下。
好接下来对举例做个一一讲解。
<?phpfunction yield_func(){ yield 12; return 'a';}$gen = yield_func();$re = $gen->current();echo 'current return : ' . $re;
输出:
current return : 12
看到 php-yield-test/generatorMothod.php
代码。
通过第一个代码事例,可得,对一个generator调用current方法,才算真正开始执行。执行到yield为止。如果不能命中yield,则执行到函数结束。
非generoator会立马执行并得到结果,而非一个生成器对象。
通过例子2,调用current一次,两次呢,第一次可以看到代码执行日志,第二次,只是把上一次的结果返回给我们而已,并不是让该生成器重新执行。
通过例子1,调用该函数还会获取到返回值,返回的内容就是 yield 表达式左边的内容。如果表达式无内容,则是NULL.
yield
点中传入一个值,并返回下一次current
值。<?phpfunction yield_func(){ $data = yield 12; echo 'get yield data: ' . $data; return 'a';}$gen = yield_func();$re = $gen->current();$gen->send(32);
输出:
get yield data: 32
例子3,是一个current,send的常规调用。调用current代码运行yield等到用户send输入参数。接收到输入后,继续运行。current能够接收到yield弹出的值,send返回值为空。
例子4,直接调用send,相当于调用current,send。不过current的返回值,并不会通过send传给用户。
例子21中,可以看到直接调用send(1),会运行生成器,并向第一个yield处输入1,继续运行至下一个yield的返回值value
。所以,$gen->send(2)
,和 $gen->current()
结果都是同一个值。
也就是说:跳过current,直接调用send,会丢失第一次yield的弹出值。
<?phpfunction yield_func(){ echo 'run to code line: ' . __LINE__ . PHP_EOL; yield; echo 'run to code line: ' . __LINE__ . PHP_EOL; return $result;}$gen = yield_func();$gen->current();echo 'current called' . PHP_EOL;$gen->next();
输出:
run to code line: 4current called run to code line: 6
例子5,这是一个较为常规的调用,调用current
代码运行yield
等到用户输入,这是调用next跳过,让代码继续运行。
例子6,直接调用next
,相当于调用current
,next
。而且通过最后打印$result
, 我们发现怎么有点像在调用 $gen->send(NULL);
。
<?phpfunction yield_func(){ echo 'run to code line: ' . __LINE__ . PHP_EOL; $result = yield 12; echo 'run to code line: ' . __LINE__ . PHP_EOL;}$gen = yield_func();echo 'call yield_func rewind ' . PHP_EOL;$gen->rewind();
输出:
call yield_func rewind run to code line: 4
例子7,8 中,发现调用该方法,会导致隐式调用current
。
例子9 中,发现在执行过一个yield代码段后,再次调用该方法,会导致报错(哪怕该 生成器已结束)。
<?phpfunction yield_func(){ try { $re = yield 'exception'; } catch (Exception $e) { echo 'catched exception msg: ' .$e->getMessage(); }}$gen = yield_func();$gen->throw(new \Exception('new yield exception'));
输出:
catched exception msg: new yield exception
通过以上简单的例子可得,throw 就是让yield这行代码产生异常,让外面的try catch 捕获我们生成的那个异常。
例子11中,构造生成器,并调用current方法,运行到yield处,再调用throw,就能捕获到异常。
例子12中,当调用send方法,跳过函数内yield代码时,再调用throw传入异常,就没法捕获了。
<?phpfunction yield_func(){ yield 12; return 'a';}$gen = yield_func();$gen->send(1);$check = $gen->valid();echo 'the generator valid ? ' . intval($check);
输出:
the generator valid ? 0
例子12中,发现current被隐式调用。
例子13中,可得,当生成器运行到yield代码段时,用valid
函数检查,都会返回true
。
所以,别问我是否已运行,问就是运行。该方法用来获取是否关闭状态,不是 是否运行状态!运行到底,运行到return就是 关闭状态。
<?phpfunction yield_func(){ yield 1 => 'abc';}$gen = yield_func();echo 'value is :' . $gen->current() . PHP_EOL;echo 'key is: ' . $gen->key() . PHP_EOL;
输出:
value is :abc key is: 1
从以上例子中,可得yield可显示设置返回的key.
例子15 中,发现key的分发规律和PHP数组键值发放策略是差不多的,默认从0开始,未指定则是以上一个数字key+1
作为当前的key
.
例子16 中,我们又发现current
被隐式调用。
<?phpfunction yield_func(){ yield 1 => 'abc';}$gen = yield_func();try {$ser = serialize($gen);} catch (\Exception $e) { print_r($e->getMessage());}
输出:
Serialization of 'Generator' is not allowed
这是一个魔术方法,见 PHP 魔术方法,也就是说 生成器 不能被序列化成一个字符串。
例子17就不用说了,看下例子18,看样子序列化成功了。也就是说一个生成器做为一个方法可以被序列化,当函数变成生成器时,就不能被序列化了。
<?phpfunction yield_func(){ yield 1 => 'abc'; return 32;}$gen = yield_func();$gen->send(0);echo 'call yield_func return, and get: ' . $gen->getReturn();
输出:
call yield_func return, and get: 32
该函数就是获取生成器最后的返回值。如果没有return语句,或者没有执行到return语句,调用该函数得到的就是NULL。
例子19 可得,getReturn 能够获取到生成器最后的返回值。
例子19、20 可得,当生成器没有执行到return语句,或者没有执行到最后时,调用getReturn是会导致报错。
到这里,我们就发现rewind
,next
和 __wakeup
这两个函数感觉没啥叼用呢,为啥还存在呢,因为Generator
继承Iterator
,自然就有了rewind
, next
方法,PHP
虽然支持方法覆盖,但子类的访问修饰符
不能缩紧,所以Generator
只能重写这两个方法。 __wakeup
继承自 stdClass
。
看图:
PHP yield 生命周期图
画了两个状态转换图,上面的要细致,繁复一点。下面的精简版,便于快速理解。
以上就是关于 PHP 生成器的基础内容,希望你看了后对它有更进一步认识。下一讲,我们手把手一起来做一个任务调度器,实战一下。
有问题欢迎提问,谢谢大家!
以上がPHP イールド コルーチン ジェネレーターの使用法を理解するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。