最初不相识,最终不相认
生成器允许你在 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
实例
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; }
|
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的内存。你根本没法把整个文件读取到内存中。
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)迭代器。
yield
和 return
都会返回值,但区别在于一个 return 是返回既定结果,一次返回完毕就不再返回新的结果,而 yield 是不断产出直到无法产出为止。
http://blog.csdn.net/bandita/article/details/47723815
https://laravel-china.org/articles/1430/single-php-generator-complete-knowledge-generator-implementation-process