yield:
对于yield方法和Generator的send同时使用时的执行顺序一直搞不清,今天看到这篇
加上测试,终于搞清了,现在举几个例子在这,以防忘记,关于yield和Generator具体是啥,干啥用,这里不说了!
1.经典的例子热身
function xrange($start, $end, $step = 1) {
for ($i = $start; $i <= $end; $i += $step) {
yield $i;
}
}
foreach (xrange(1, 1000000) as$num) {
echo$num, "\n";
}
$range = xrange(1, 1000000);
var_dump($range); // object(Generator)#1
var_dump($range instanceof Iterator); // bool(true)
2.鸟哥博客中的例子
在PHP中使用协程实现多任务调度,(确定要搞清楚为什按照这种方式输出. 以便后续继续阅读.的例子)在上边连接文章里有详细的解释了。
3.一个读取文件的例子,同时使用了send函数
/* a.log的内容 */ aaaaa bbbbb ccccc ddddd
function lineGenerator($file) { $fp = fopen($file, ‘rb‘); while ($line = fgets($fp)) { var_dump(yield $line); } } $lines = lineGenerator("a.log"); foreach ($linesas$line) { $lines->send(‘test‘); echo$line; }
/* 输出结果 */string(4) "test" aaaaa NULLstring(4) "test" ccccc NULL
之前对于这个输出结果一直理解不了,现在总结一下怎么分析:
1.外层循环的每一次都会调用一次内层函数中yield的一行,执行完一次yield便停止执行。
2.对于var_dump(yield $line) 这样的写法,在分析时可以拆分为$var = (yield $line);var_dump($var);便于理解。
3.send()函数在调用时如果yield函数一次也没被执行过,则会先执行一次yield,再进行赋值,再执行next(send有一个next的功能)。
根据上边的方法分析一下上边的例子:
1.首先,根据总结的方法2先对lineGenerator函数进行修改,因为a.log就4行,索性可以不用循环了。修改后如下:
function lineGenerator($file) { $fp = fopen($file, ‘rb‘); $var = (yield fgets($fp)); var_dump($var); $var = (yield fgets($fp)); var_dump($var); $var = (yield fgets($fp)); var_dump($var); $var = (yield fgets($fp)); var_dump($var); }
2.foreach开始时,($lines as $line)肯定是调用了current()方法赋值$line,就是内存循环要执行一次yield,此时内层代码执行到第二行,$line被赋值aaaaa。
3.接着,外层循环调用了$lines->send(‘test‘),这时的test值会赋值给内层函数当前的yield(就是第一个yield),然后执行第一个var_dump(),打印出了test,然后执行第二个yield,并把yield的值赋给send函数的返回值(就是bbbbb),内层代码停止。
4.接着,外层循环执行了echo $line;所以打印出aaaaa。
5.foreach 进入下次循环,就是需要从函数上次停下的位置执行到下一个yield执行完,函数先执行第二个var_dump(),此时当前的yield是NULL,所 以打印出NULL,接着执行第三个yield,获取到a.log的第三行赋值给$line,即ccccc,函数执行停止。
6.然后循环执行$lines->send(‘test‘),函数的第三个var_dump()就打印出test,执行第4个yield,把a.log的ddddd赋给send的返回值。函数执行停止。
7.外层循环执行echo $line;打印出ccccc。
8.foreach进入下一次循环,函数又要从上次停止的位置执行到下一个yield结束,就是函数中最后一个var_dump()执行,打印出NULL,因为后边没有yield了,代码执行结束。
4.一个日志写入的例子,可以说明调用send时没有调用过yield的情况,用总结的第三个方法解决。
function logger($fileName) { $fileHandle = fopen($fileName, ‘a‘); while (true) { echo "aaa\n"; fwrite($fileHandle, yield."\n"); echo "bbb\n"; } } $logger = logger(‘a.log‘); var_dump($logger->send(‘Foo‘)); var_dump($logger->send(‘Bar‘));
/* 输出结果*/ aaa bbb aaa NULL bbb aaa NULL
分析:
1.外部第一行$logger = logger(‘a.log‘),此时只是生成了一个Generator对象,yield并没有执行。
2.外部第二行$logger->send(‘Foo‘),由于函数内部没有yield,所以会先执行一次yield后再执行赋值,next()操作。执行一次yield,会打印出aaa,注意yield执行完但是fwrite并没有执行,(这个可以算是send函数的初始化操作,哈哈)
接着才是像正常情况下的send执行一样,进行next()操作,先把Foo赋值给当前的yield,执行fwrite写入,然后打印bbb,再打印aaa,执行
fwrite($fileHandle,yield . "\n");注意fwrite不执行,由于yield后边是空的,所以send的返回值赋值为NULL,内部函数停止,外部的第一个var_dump()会打印一个NULL。
$lineParts = explode(‘ ‘, $line, 2); yield $lineParts[0] => $lineParts[1];
原文:http://www.cnblogs.com/leezhxing/p/4958541.html