这篇文章上次修改于 3135 天前,可能其部分内容已经发生变化,如有疑问可询问作者。
1、什么是生成器
Generator提供了一种方便的实现简单的Iterator(迭代器)的方式,使用Generator实现Iterator不需要创建一个类来继承Iterator接口。具体可参考:理解PHP中的Generator
生成器函数的核心是yield关键字。它最简单的调用形式看起来像一个return申明,不同之处在于普通return会返回值并终止函数的执行,而yield会返回一个值给循环调用此生成器的代码并且只是暂停执行生成器函数
2、看一段代码
<?php
function gen() {
$ret = (yield 'yield1');
var_dump($ret);
$ret = (yield 'yield2');
var_dump($ret);
}
$gen = gen();
var_dump($gen->current()); // string(6) "yield1"
var_dump($gen->send('ret1')); // string(4) "ret1" (the first var_dump in gen)
// string(6) "yield2" (the var_dump of the ->send() return value)
var_dump($gen->send('ret2')); // string(4) "ret2" (again from within gen)
// NULL (the return value of ->send())
?>
解释:
上面的代码首先是调用函数gen生成一个Generator对象,然后调用这个对象的current方法返回第一个值,
显然它是第一个yield语句的返回值,也就是'yield1',这个时候gen函数的执行就会被中止,接着执行 var_dump($g->send('ret1'));
调用$g->send('ret1'),传入参数为字符串'ret1',按照上面的说明,它会赋值给第一个yield表达式,也就是(yield 'yield1')中的yield(注意:这个时候不包括'yield1'),它的值为'ret1',然后会赋值给$ret,所以第二个输出'ret1'就是gen函数中的第一个var_dump输出的。此时对Generator对象的迭代会恢复继续执行,实际上就是调用了一次next函数,它会执行到下一个yield语句:yield 'yield2',这个语句会返回'yield2',它会作为$g->send('ret1')的返回值,所以函数外第二个var_dump会输出'yield2'。
最后再次调用send函数,这次传入的参数为字符串'ret2',跟上面一样,Generator对象当前位置的元素是在gen函数的第二个yield上,所以’ret2'会被传递给第二个yield表达式,也就是作为(yield 'yield2')中的yield的值,并且会被赋值给$ret变量,然后gen函数恢复执行,它会执行gen函数中的最后一个var_dump,此时对Generator对象$g的遍历也结束了,第二个send函数的返回值为NULL,这也是函数外的最后一个var_dump的输出
3、再看一段代码
<?php
function nums() {
for ($i = 0; $i < 5; ++$i) { //get a value from the caller
$cmd = (yield $i);
if($cmd == 'stop') return;//exit the function
}
}
$gen = nums();
foreach($gen as $v) {
if($v == 3)//we are satisfied
$gen->send('stop');
echo "{$v}\n";
}
//Output 0 1 2 3
?>
解释:
1、yield作为语句(类似return语句),会返回$i给调用者。
2、yield作为表达式。获取send函数传递值,赋值给$cmd。
3、实现Generator对象和generator函数的通信。这个很重要。应该能实现很多
已有 41 条评论