php生成器函数与yield关键字
初次接触迭代器与生成器是在Python中,之后了解到在 php5.5 中也引入了生成器的特性,
但很多PHP开发者或许都不知道生成器这个功能,可能是因为平时使用场景较少吧。
但是,生成器功能的确非常有用。
优点:
生成器会对PHP应用的性能有非常大的影响
PHP代码运行时节省大量的内存
比较适合计算大量的数据
使用一个简单的例子说明(迭代输出从1开始到10000的数组,步进为1):
<?php$start_mem = memory_get_usage();$arr = range( 1, 10000 );foreach( $arr as $value ){ //echo $value.',';}$end_mem = memory_get_usage();echo " use mem : ". ( $end_mem - $start_mem ) .'bytes'.PHP_EOL;
输入结果:
<?php$start_mem = memory_get_usage();function xrange($start, $limit, $step = 1) { for ($i = $start; $i <= $limit; $i += $step) { // 注意变量$i的值在不同的yield之间是保持传递的。 yield $i; }}foreach( xrange( 0, 10000 ) as $value ){ echo $value.PHP_EOL;}$end_mem = memory_get_usage();echo " use mem : ". ( $end_mem - $start_mem ) .'bytes'.PHP_EOL;
输入结果:
首先从运行结果上来看,528440bytes
与32bytes
这两个内存消耗就一目了然了,
在生成器中提供了一种更容易的方法来实现简单的对象迭代(循环),
相比较定义类实现 Iterator
接口的方式,性能开销和复杂性大大降低。
相关代码剖析
这里使用示例代码,在命令行下执行,加上 sleep(1)
可使执行结果更加明显
<?phpfunction xrange($start, $limit, $step = 1) { // echo '生成器开始执行' . PHP_EOL; for ($i = $start; $i <= $limit; $i += $step) { // echo '产生数据之前:' . $i . PHP_EOL; yield $i; // echo '产生数据之后:' . $i . PHP_EOL; } // echo '再来一个数据' . PHP_EOL; yield 100; // echo '生成器执行结束' . PHP_EOL;}$arr = xrange( 0, 5 );// echo '生成器开始执行了吗?' . PHP_EOL;sleep(1);foreach( $arr as $value ){ sleep(1); // echo '使用数据前' . PHP_EOL; echo '使用数据:' . $value . PHP_EOL; // echo '使用数据后' . PHP_EOL;}
输出结果:
使用数据:0使用数据:1使用数据:2使用数据:3使用数据:4使用数据:5使用数据:100
我们可以看到数据在一行一行的输出,接着我们去掉代码中的注释,再次执行一遍
输出结果:
生成器开始执行了吗?生成器开始执行产生数据之前:0使用数据前使用数据:0使用数据后产生数据之后:0产生数据之前:1使用数据前使用数据:1使用数据后产生数据之后:1...产生数据之前:5使用数据前使用数据:5使用数据后产生数据之后:5再来一个数据使用数据前使用数据:100使用数据后生成器执行结束
还原一下代码执行过程:
首先调用
xrange
函数(生成器),传入( 0, 5 )
,这里我们看到生成器并没有开始执行foreach
开始对$arr
循环,执行生成器,接着for
产生第一个数据,将数据0
返回到foreach
中,第一次for
循环结束foreach
准备第二次循环,接着for
产生第二个数据,将数据1
返回到foreach
中,第二次foreach
循环结束,第二次for
循环结束这里
foreach
循环循环6
次,for
循环六次,至此for
循环结束foreach
循环第七次,输出生成器中最后一个数100
,到此foreach
循环结束
从代码中我们看到,始终只有一个记录值参与循环,内存中也只有一条信息。
无论开始传入的 $arr
有多大,由于并不会立即生成所有结果集,所以内存始终是一条循环的值
生成器函数的核心 – yield关键字
yield
最简单的调用形式看起来像一个 return
申明,不同之处在于普通 return
会返回值并终止函数的执行,而 yield
会返回一个值给循环调用此生成器的代码并且只是暂停执行生成器函数。。
概念理解
到这里,你应该已经大概理解什么是生成器了。下面我们来说下生成器原理。
首先明确一个概念:生成器 yield
关键字不是返回值,他的专业术语叫产出值,只是生成一个值
那么代码中 foreach
循环的是什么?其实是PHP在使用生成器的时候,会返回一个 Generator
类的对象。foreach
可以对该对象进行迭代,每一次迭代,PHP会通过 Generator
实例计算出下一次需要迭代的值。这样 foreach
就知道下一次需要迭代的值了。
而且,在运行中 for
循环执行后,会立即停止。等待 foreach
下次循环时候再次和 for
索要下次的值的时候,for
循环才会再执行一次,然后立即再次停止。直到不满足条件不执行结束。
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: