Generator
-
官方文档
-
生成器提供了一种更容易的方法来实现简单的对象迭代,相比较定义类实现
Iterator
接口的方式,性能开销和复杂性大大降低。
生成器允许你在foreach
代码块中写代码来迭代一组数据而不需要在内存中创建一个数组,那会使你的内存达到上限,或者会占据可观的处理时间。相反,你可以写一个生成器函数,就像一个普通的自定义函数一样,和普通函数只返回一次不同的是, 生成器可以根据需要 yield 多次,以便生成需要迭代的值。
注意:PHP version 5.5+
Generator实现了Iterator接口
Generator implements Iterator {
//返回当前产生的值
public mixed current ( void )
//返回当前产生的键
public mixed key ( void )
//生成器继续执行
public void next ( void )
//重置迭代器,如果迭代已经开始了,这里会抛出一个异常。
public void rewind ( void )
//向生成器中传入一个值,当前yield接收值,然后继续执行下一个yield
public mixed send ( mixed $value )
//向生成器中抛入一个异常
public void throw ( Exception $exception )
//检查迭代器是否被关闭,已被关闭返回 FALSE,否则返回 TRUE
public bool valid ( void )
//序列化回调
public void __wakeup ( void )
//返回generator函数的返回值,PHP version 7+
public mixed getReturn ( void )
}
生成器函数的核心是yield关键字。它最简单的调用形式看起来像一个return申明,不同之处在于普通return会返回值并终止函数的执行,而yield会返回一个值给循环调用此生成器的代码并且只是暂停执行生成器函数。 NOTE:
- 一个生成器不可以返回值:这样做会产生一个编译错误。然而return空是一个有效的语法并且它将会终止生成器继续执行。
下面是一个简单例子:
function xrange($start, $end, $step = 1) {
for ($i = $start; $i <= $end; $i += $step) {
yield $i;
}
}
$xrang = xrange(1, 3);
print_r($xrang); //Generator Object( )
var_dump($xrang instanceof Iterator); // bool(true)
//依次输出1 2 3
foreach ($xrang as $value) {
echo $value."\n";
}
调用xrang()的时候,里面的代码并没有真正的执行,而是返回了一个生成器对象$xrang = Generator Object( )
,$xrang instanceof Iterator
说明Generator实现了Iterator接口,可以用foreach进行遍历,每次遍历都会隐式调用current()、next()、key()、valid()
等方法。
第一次调用Generator的时候,会隐式调用rewind(),如果已经开始迭代了,则会抛出异常Cannot rewind a generator that was already run
,
yield
yield 只能在函数中使用,否则会报PHP Fatal error:The "yield" expression can only be used inside a function
,凡是使用了yield关键字的函数都会返回一个Generator对象。
每次代码执行到yield语句都会中止执行,返回yield语句中表达式的值给Generator对象,继续迭代Generator对象时,yield后面的代码会接着执行,直到所有yield语句全部执行完毕或者有return语句,这个renturn语句只能返回nullreturn;
,否则会编译错误。
Generator::send(mixed $value)
向生成器中传入一个值,并且当做yield表达式的结果,然后继续执行生成器。如果当这个方法被调用时,生成器不在 yield 表达式,那么在传入值之前,它会先运行到第一个 yield 表达式。
function test()
{
$a = (yield 111);
var_dump('test()->$a:'.$a);
$b = (yield 222);
var_dump('test()->$b:'.$b);
}
$gen = test();
echo "第一次输出:\n";
var_dump($gen->current());
echo "第二次输出:\n";
var_dump($gen->send(333));
echo "第三次输出:\n";
var_dump($gen->next());
/** 输出结果
第一次输出:
int(111)
第二次输出:
string(14) "test()->$a:333"
int(222)
第三次输出:
string(11) "test()->$b:"
NULL
*/
第一次输出 111 说明代码运行到第一个yield,并返回值111,此时函数的执行被中止;
调用$gen->send(333)
时,第二次输出test()->$a:333 和 222
,说明把333发送给了当前yield表达式,也就是第一个出现yield的位置,把值赋给了变量$a,并输出了变量$a,之后又输出了222,说明Generator对象$gen的迭代恢复执行,执行到下一个yield表达式yield 222
,实际就是调用了next(),返回值222,当前的yield表达式迭代到第二个yield位置;
第三次输出test()->$b: 和 NULL
,此时迭代器恢复执行,表达式(yield 222)
没有收到任何参数值,所以$b为NULL。