初次接触迭代器与生成器是在Python中,之后了解到在 php5.5 中也引入了生成器的特性,
但很多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)
可使执行结果更加明显
<?php
function 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
最简单的调用形式看起来像一个 return
申明,不同之处在于普通 return
会返回值并终止函数的执行,而 yield
会返回一个值给循环调用此生成器的代码并且只是暂停执行生成器函数。。
到这里,你应该已经大概理解什么是生成器了。下面我们来说下生成器原理。
首先明确一个概念:生成器 yield
关键字不是返回值,他的专业术语叫产出值,只是生成一个值
那么代码中 foreach
循环的是什么?其实是PHP在使用生成器的时候,会返回一个 Generator
类的对象。foreach
可以对该对象进行迭代,每一次迭代,PHP会通过 Generator
实例计算出下一次需要迭代的值。这样 foreach
就知道下一次需要迭代的值了。
而且,在运行中 for
循环执行后,会立即停止。等待 foreach
下次循环时候再次和 for
索要下次的值的时候,for
循环才会再执行一次,然后立即再次停止。直到不满足条件不执行结束。
在下一篇文章中我们介绍一下生成器的具体使用
1、生成器总览 与 生成器语法
2、PHP的yield是个什么玩意
3、PHP中被忽略的性能优化利器:生成器
本文为冯奎原创文章,转载无需和我联系,但请注明来自冯奎博客fengkui.net
最新评论