PHP迭代器和生成器用法实例分析


Posted in PHP onSeptember 28, 2019

本文实例讲述了PHP迭代器和生成器用法。分享给大家供大家参考,具体如下:

迭代器

迭代器实际是一个实现了Iterator的类,可以用foreach进行遍历。

例如:

<?php
class Sample implements Iterator{
  private $curIndex=0;
  private $items=null;
  public function __construct($_items) {
    $this->items = $_items;
  }
  public function current (){
    echo "current\n";
    return $this->items[$this->curIndex];
  }
  public function key (){
    echo "key\n";
    return $this->curIndex;
  }
  public function next (){   
      echo "next\n"; 
      $this->curIndex++;
  }
  public function rewind (){
      $this->curIndex = 0;   
  }
  public function send ( $value ){
    if($value == "stop"){
      $this->curIndex = null;
    }
  }
  public function valid (){
    echo "valid\n";
    return isset($this->items[$this->curIndex]);
  }
}
$sample = new Sample([1,2,3]);
foreach ($sample as $k =>$v){
}

输出

 valid current key next

可以看到foreach 是先调用valid判断迭代器是否有效,然后再调用current获取当前值,同时调用next移动key到指向下一个值(输出key是因为 $k=>$v的缘故)。

生成器

让我们先看一下官方文档

生成器提供了一种更容易的方法来实现简单的对象迭代,相比较定义类实现 Iterator 接口的方式,性能开销和复杂性大大降低。
生成器允许你在 foreach 代码块中写代码来迭代一组数据而不需要在内存中创建一个数组, 那会使你的内存达到上限,或者会占据可观的处理时间。
相反,你可以写一个生成器函数,就像一个普通的自定义函数一样, 和普通函数只返回一次不同的是, 生成器可以根据需要 yield 多次,以便生成需要迭代的值。
PHP 将会在每次需要值的时候调用生成器函数,并在产生一个值之后保存生成器的状态,这样它就可以在需要产生下一个值的时候恢复调用状态。

下面是php官方文档中的示例

<?php
function gen_one_to_three() {
  for ($i = 1; $i <= 3; $i++) {
    //注意变量$i的值在不同的yield之间是保持传递的。
    yield $i;
  }
}
$generator = gen_one_to_three();
foreach ($generator as $value) {
  echo "$value\n";
}
var_dump($generator); //实际上是Generator对象

如上,若把3修改成10000,对于$generator实际上没有区别,它只是保存了一个当前值(当然还有相关的内部状态,这里是为了简化),并没有产生10000个数。

从中可以看出生成器的优势在于减少内存的使用,在需要时才生成对应的值。

查看php文档,我们可以看到Generator实际也是Iterator的具体实现,yield调用时就是返回的Generator对象。

那么怎么理解迭代器和生成器的关系呢?

其实,生成器是迭代器的实现+yield,产生了生成器对象。

我们也可以自己定义一个类似yield的函数,如下:

function myYeild(){
  $args = func_get_args();
  return new Sample($args);
}
$generator = myYeild(1,2,3);
foreach ($generator as $value) {
  echo "$value\n";
}

注意,我们的myYeild,是不能和php内置的yeild那么使用的,因为yeild会保存调用上下文,临时离开,并没有return。

这里只是类比一下。

既然yeild可以把普通的对象包装成generator,那么我们的iterator通过yeild也可以像Generator一样吗?

答案有点悲伤,yeild是把传入的值作为参数生成Generator实例,它并不知道我们的iterator。不过这样设计也是合理的,
以防我们自己的iterator不靠谱。

实际使用场合

  • 数据库遍历

可以结合游标,遍历数据库时,不需要一次返回所有数据,而是每次取一行。

class AllUser implements \Iterator
{
  protected $index = 0;
  protected $data = [];
  public function __construct()
  {
    $link = mysqli_connect('192.168.0.91', 'root', '123', 'xxx');
    $rec = mysqli_query($link, 'select id from doc_admin');
    $this->data = mysqli_fetch_all($rec, MYSQLI_ASSOC);
  }
  //1 重置迭代器
  public function rewind()
  {
    $this->index = 0;
  }
  //2 验证迭代器是否有数据
  public function valid()
  {
    return $this->index < count($this->data);
  }
  //3 获取当前内容
  public function current()
  {
    $id = $this->data[$this->index];
    return User::find($id);
  }
  //4 移动key到下一个
  public function next()
  {
    return $this->index++;
  }
  //5 迭代器位置key
  public function key()
  {
    return $this->index;
  }
}
//实现迭代遍历用户表
$users = new AllUser();
//可实时修改
foreach ($users as $user){
  $user->add_time = time();
  $user->save();
}
  • 文件遍历
    一次读取一行
  • 实现Iterator接口,让普通类可以使用foreach遍历。
  • 协程,参见鸟哥则这篇文章。

注意:可以在生成器的函数前加"&",可以使用引用。在函数里直接return会终止生成器。

希望本文所述对大家PHP程序设计有所帮助。

PHP 相关文章推荐
PHP利用COM对象访问SQLServer、Access
Oct 09 PHP
不错的PHP学习之php4与php5之间会穿梭一点点感悟
May 03 PHP
编写漂亮的代码 - 将后台程序与前端程序分开
Apr 23 PHP
apache mysql php 源码编译使用方法
May 03 PHP
PHP 冒泡排序 二分查找 顺序查找 二维数组排序算法函数的详解
Jun 25 PHP
php实现的双向队列类实例
Sep 24 PHP
ThinkPHP模板之变量输出、自定义函数与判断语句用法
Nov 01 PHP
php使用memcoder将视频转成mp4格式的方法
Mar 12 PHP
PHP预定义变量9大超全局数组用法详解
Apr 23 PHP
PHPMailer使用QQ邮箱实现邮件发送功能
Aug 18 PHP
Laravel 实现关系模型取出需要的字段
Oct 10 PHP
Yii框架布局文件的动态切换操作示例
Nov 11 PHP
php实现的数组转xml案例分析
Sep 28 #PHP
PHP反射原理与用法深入分析
Sep 28 #PHP
Windows服务器中PHP如何安装redis扩展
Sep 27 #PHP
php-fpm超时时间设置request_terminate_timeout资源问题分析
Sep 27 #PHP
thinkPHP+LayUI 流加载实现功能
Sep 27 #PHP
PHP的cookie与session原理及用法详解
Sep 27 #PHP
PHP下载文件函数与用法示例
Sep 27 #PHP
You might like
Zend引擎的发展 [15]
2006/10/09 PHP
使用bcompiler对PHP文件进行加密的代码
2010/08/29 PHP
PHP实现图片裁剪、添加水印效果代码
2014/10/01 PHP
php+ajax注册实时验证功能
2016/07/20 PHP
php版微信公众平台接口参数调试实现判断用户行为的方法
2016/09/23 PHP
PHP实现简单ajax Loading加载功能示例
2016/12/28 PHP
PHP 扩展Memcached命令用法实例总结
2020/06/04 PHP
用javascript实现的激活输入框后隐藏初始内容
2007/06/29 Javascript
基于jQuery的history历史记录插件
2010/12/11 Javascript
自己动手开发jQuery插件教程
2011/08/25 Javascript
NodeJS中利用Promise来封装异步函数
2015/02/25 NodeJs
jQuery实现可用于博客的动态滑动菜单
2015/03/09 Javascript
JavaScript实现找质数代码分享
2015/03/24 Javascript
使用AngularJS处理单选框和复选框的简单方法
2015/06/19 Javascript
javascript实现跨域的方法汇总
2015/06/25 Javascript
jQuery表格(Table)基本操作实例分析
2017/03/10 Javascript
总结javascript三元运算符知识点
2018/09/28 Javascript
element-ui的回调函数Events的用法详解
2018/10/16 Javascript
微信小程序实现的自定义分享功能示例
2019/02/12 Javascript
详解element-ui级联菜单(城市三级联动菜单)和回显问题
2019/10/02 Javascript
Python实现获取网站PR及百度权重
2015/01/21 Python
python定时利用QQ邮件发送天气预报的实例
2017/11/17 Python
Python一行代码解决矩阵旋转的问题
2019/11/30 Python
Python+OpenCV+图片旋转并用原底色填充新四角的例子
2019/12/12 Python
python getopt模块使用实例解析
2019/12/18 Python
Pycharm配置PyQt5环境的教程
2020/04/02 Python
如何利用python读取micaps文件详解
2020/10/18 Python
Python批量修改xml的坐标值全部转为整数的实例代码
2020/11/26 Python
Python爬虫之Selenium多窗口切换的实现
2020/12/04 Python
英国第一摩托车和摩托车越野配件商店:GhostBikes
2019/03/10 全球购物
在线实验室测试:HealthLabs.com
2020/05/03 全球购物
2014年国培研修感言
2014/03/09 职场文书
假面舞会策划方案
2014/05/29 职场文书
CSS3实现三角形不断放大效果
2021/04/13 HTML / CSS
Python破解极验滑动验证码详细步骤
2021/05/21 Python
Python编程中Python与GIL互斥锁关系作用分析
2021/09/15 Python