PHP生成器的简单理解

最初不相识,最终不相认
生成器允许你在 foreach 代码块中写代码来迭代一组数据而不需要在内存中创建一个数组, 那会使你的内存达到上限,或者会占据可观的处理时间。相反,你可以写一个生成器函数,就像一个普通的自定义函数一样, 和普通函数只返回一次不同的是, 生成器可以根据需要 yield 多次,以便生成需要迭代的值。 一个简单的例子就是使用生成器来重新实现 range() 函数。 标准的 range() 函数需要在内存中生成一个数组包含每一个在它范围内的值,然后返回该数组, 结果就是会产生多个很大的数组。 比如,调用 range(0, 1000000) 将导致内存占用超过 100 MB。 做为一种替代方法, 我们可以实现一个 xrange() 生成器, 只需要足够的内存来创建 Iterator 对象并在内部跟踪生成器的当前状态,这样只需要不到1K字节的内存。 ### 简单理解
1
2
3
4
5
6
官方的定义中:生成器就是简单的迭代器。
不同于标准的PHP迭代器,PHP的生成器并不要求你一定要在一个重量级的类中实现Iterator接口。
相反,生成器会按需求计算并交出迭代值。这对应用程序的性能有着深远的意义。想想吧,
一个标准的PHP迭代器通常都是基于内存中全部计算过的数据集进行迭代。这样做效率不高。
尤其是大量可计算的公式化数据集。使用生成器我们可以动态的计算并交出下一个值而不占用
宝贵的系统内存,这就是我们使用生成器的原因。
### 创建一个生成器
1
2
3
4
5
6
<?php  
function myGenerator() {
yield 'value1';
yield 'value2';
yield 'value3';
}
### 使用这个生成器
1
2
3
4
<?php  
foreach (myGenerator() as $yieldedValue) {
echo $yieldedValue, PHP_EOL;
}
结果: value1 value2 value3

实例

  • Range生成器(糟糕的实现)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php  
function makeRange($length) {
$dataset = [];
for ($i = 0; $i < $length; $i++) {
$dataset[] = $i;
}

return $dataset;
}

$customRange = makeRange(1000000);
foreach ($customRange as $i) {
echo $i, PHP_EOL;
}

  • Range生成器(好的实现)
1
2
3
4
5
6
7
8
9
10
11
<?php  
function makeRange($length) {
for ($i = 0; $i < $length; $i++) {
yield $i;
}
}

foreach (makeRange(1000000) as $i) {
echo $i, PHP_EOL;
}

上面的例子并不实用。但是,联想一下所有你可能用来计算的数据集。序列数(例如斐波纳挈数)就是很典型的例子。同样你还可以遍历文件流资源。想象一下,如果你需要遍历一个4G的逗号分隔值(CSV)文件,而你的虚拟个人服务器(VPS)上PHP只分配了1G的内存。你根本没法把整个文件读取到内存中。

  • CSV生成器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php  
function getRows($file) {
$handle = fopen($file, 'rb');
if ($handle === false) {
throw new Exception();
}
while (feof($handle) === false) {
yield fgetcsv($handle);
}
fclose($handle);
}

foreach (getRows('data.csv') as $row) {
print_r($row);
}

接收参数

1
2
3
4
5
6
7
8
9
10
11
function printer()
{
while (true) {
printf("receive: %s\n", yield);
}
}

$printer = printer();

$printer->send('hello');
$printer->send('world');

说明

生成器的确使用了很少的内存却大大简化了某些任务。如果你需要更强大的功能诸如重置、快进、或者数据集的检索,你最好还是自定义一个实现Iterator interface接口的类,或者使用PHP现成的Standard PHP Library(SPL)迭代器。

yieldreturn 都会返回值,但区别在于一个 return 是返回既定结果,一次返回完毕就不再返回新的结果,而 yield 是不断产出直到无法产出为止。

http://blog.csdn.net/bandita/article/details/47723815
https://laravel-china.org/articles/1430/single-php-generator-complete-knowledge-generator-implementation-process