yield生成器
介绍
一个简单的例子就是使用生成器来重新实现 range() 函数。 标准的 range() 函数需要在内存中生成一个数组包含每一个在它范围内的值,然后返回该数组, 结果就是会产生多个很大的数组。 比如,调用 range(0, 1000000) 将导致内存占用超过 100 MB。
做为一种替代方法, 我们可以实现一个 xrange() 生成器, 只需要足够的内存来创建 Iterator 对象并在内部跟踪生成器的当前状态,这样只需要不到1K字节的内存。
<?php
function xrange($start, $limit, $step = 1) {
if ($start < $limit) {
if ($step <= 0) {
throw new LogicException('Step must be +ve');
}
for ($i = $start; $i <= $limit; $i += $step) {
yield $i;
}
} else {
if ($step >= 0) {
throw new LogicException('Step must be -ve');
}
for ($i = $start; $i >= $limit; $i += $step) {
yield $i;
}
}
}
性能测试
使用下面这段代码测试,发现有几个现象:
一、当$num的值大到一定阀值时range方式会报fatal error
Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 134217736 bytes) in /Users/liuyidong/Documents/htdocs/hr.wiiqq.com/tmp/yield/test.php on line 6
二、yield方式会随着$num的值越大耗时越久,内存一直维持在0
<?php
$num = 700000;
$time_start = microtime_float();
$start = memory_get_usage();
echo 'range方式 : ';
foreach (range(1, $num, 2) as $number) {
// echo "$number ";
}
$memory = sprintf("%7.5f", memory_get_usage() - $start);
$time = sprintf("%7.5f", microtime_float() - $time_start);
echo "memory[{$memory}],time[{$time}]\n";
echo 'yield方式 : ';
$time_start = microtime_float();
$start = memory_get_usage();
foreach (xrange(1, $num, 2) as $number) {
// echo "$number ";
}
$memory = sprintf("%7.5f", memory_get_usage() - $start);
$time = sprintf("%7.5f", microtime_float() - $time_start);
echo "memory[{$memory}],time[{$time}]\n";
function microtime_float() {
list($usec, $sec) = explode(" ", microtime());
return ((float) $usec + (float) $sec);
}
语法
当一个生成器被调用的时候,它返回一个可以被遍历的对象.当你遍历这个对象的时候(例如通过一个foreach循环),PHP 将会在每次需要值的时候调用生成器函数,并在产生一个值之后保存生成器的状态,这样它就可以在需要产生下一个值的时候恢复调用状态。
一旦不再需要产生更多的值,生成器函数可以简单退出,而调用生成器的代码还可以继续执行,就像一个数组已经被遍历完了。
Note:
一个生成器不可以返回值: 这样做会产生一个编译错误。然而return空是一个有效的语法并且它将会终止生成器继续执行。
yield关键字
生成器函数的核心是yield关键字。它最简单的调用形式看起来像一个return申明,不同之处在于普通return会返回值并终止函数的执行,而yield会返回一个值给循环调用此生成器的代码并且只是暂停执行生成器函数。
简单例子
<?php
foreach (
(function () {
for ($i = 1; $i <= 3; $i ++) {
//注意变量$i的值在不同的yield之间是保持传递的。
yield $i;
}
})() as $value
) {
echo "$value\n";
}
输出结果:
1
2
3
网友评论