PHP迭代生成器-yield

Jackey PHP 2,893 次浏览 , , 2条评论

迭代生成器

生成器的核心是一个 yield 关键字,一个生成器函数看起来像一个普通的函数,不同的是:普通函数返回一个值,而一个生成器可以 yield 生成许多它所需要的值。生成器函数被调用时,返回的是一个可以被遍历的对象。yield return 有点类似,不过不同的是, return 会返回值并且终止代码的执行,而 yield 会返回一个值给循环调用此生成器的代码并且只是暂停执行生成器函数 。

  1. <?php
  2. function gen_one_to_three()
  3. {
  4. for ($i = 1; $i <= 3; $i++) {
  5. //注意变量$i的值在不同的yield之间是保持传递的。
  6. yield $i;
  7. }
  8. }
  9.  
  10. $generator = gen_one_to_three();
  11. var_dump($generator);
  12. echo "<br/>";
  13. var_dump($generator instanceof Iterator); // bool(true)
  14. echo "<br/>";
  15. foreach ($generator as $value) {
  16. echo "$value\n";
  17. }

调用 gen_one_to_three() 的时候,里面的代码并没有真正的执行,而是返回了一个生成器对象 $generator = Generator Object()$generator instanceof Iterator 说明 Generator 实现了 Iterator 接口,可以用 foreach 进行遍历,每次遍历都会隐式调用current()next()key()valid() 等方法。(Generator类中的方法)

Generator类中的方法

  1. <?php
  2. Generator implements Iterator {
  3. public mixed current(void)//返回当前产生的值
  4. public mixed key(void) //返回当前产生的键
  5. public void next(void)//生成器继续执器public void rewind ( void ) //重置迭代器,如果迭代已经开始,这会抛出一个异常。
  6. public mixed send(mixed $value ) //向生成器中传入一个值,当前yield接收值,然后继续执行下一个yield
  7. public void throw (Exception $exception ) //向生成器中抛入一个异常
  8. public bool valid(void) //检查迭代器是否被关闭,已被关闭返回 FALSE,否则返回 TRUE
  9. public void __wakeup(void) //序列化回调
  10. public mixed getReturn(void)//返回generator函数的返回值,PHP version 7 +
  11. }

处理大数据

下面通过实现一个xrange函数来简单说明:

  1. <?php
  2. function xrange($start, $end, $step = 1)
  3. {
  4. for ($i = $start; $i <= $end; $i += $step) {
  5. yield $i;
  6. }
  7. }
  8.  
  9. foreach (xrange(1, 1000000) as $num) {
  10. echo $num, "\n";
  11. }

上面这个xrange()函数提供了和PHP的内建函数range()一样的功能.但是不同的是range()函数返回的是一个包含值从1100万的数组 而xrange()函数返回的是依次输出这些值的一个迭代器, 而不会真正以数组形式返回.这种方法的优点是显而易见的.它可以让你在处理大数据集合的时候不用一次性的加载到内存中.甚至你可以处理无限大的数据流.

处理大文件

来优化下读取大文件,在 PHP 读取大文件的时候,经常会出现内存不足的情况,如果文件过大的话,没法一次读取完,采用 yield 来实现大文件的读取。

老式读取

  1. <?php
  2. function readLocalFile($fileName)
  3. {
  4. $handle = fopen($fileName, 'r');
  5. $lines = [];
  6. while (!feof($handle)) {
  7. $lines[] = fgets($handle);
  8. }
  9. fclose($handle);
  10. return $lines;
  11. }

yield 读取方式

  1. <?php
  2. function readYieldFile($fileName)
  3. {
  4. $handle = fopen($fileName, 'r');
  5. while (!feof($handle)) {
  6. yield fgets($handle);
  7. }
  8. fclose($handle);
  9. }

为了便于测试,我们写一个读取内存的辅助函数

  1. function formatBytes($bytes)
  2. {
  3. if ($bytes < 1024) {
  4. return $bytes . "b";
  5. } else if ($bytes < 1048576) {
  6. return round($bytes / 1024, 2) . "kb";
  7. }
  8. return round($bytes / 1048576, 2) . 'mb';
  9. }

测试

  1. # 第一种readLocalFile('./all.txt');
  2. echo formatBytes(memory_get_peak_usage());
  3. # 第二种
  4. $lines = readYieldFile('./all.txt');
  5. foreach ($lines as $row) {}
  6. echo formatBytes(memory_get_peak_usage());

总结

使用老式读取,返回的是一个包含每行数据的数组,而yield方式则返回的是一个迭代器,而不会以真正的数组返回。

这种方法的优点是显而易见的.它可以让你在处理大数据集合的时候不用一次性的加载到内存中.甚至你可以处理无限大的数据流.

2条评论

  1. dandan 2020年3月21日 下午11:38 回复

    老弟从官方文档复制过来的?

    • gopher 2020年3月22日 上午10:18 回复

      没注意官方文档有没有,是一套视频教程整理的。

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

Go