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 生成静态页面的办法与实现代码详细版
Feb 15 PHP
php数组函数序列之sort() 对数组的元素值进行升序排序
Nov 02 PHP
PHP 第三节 变量介绍
Apr 28 PHP
input file获得文件根目录简单实现
Apr 26 PHP
深入PHP与浏览器缓存的分析
Jun 03 PHP
PHP获取MAC地址的具体实例
Dec 13 PHP
你可能不知道PHP get_meta_tags()函数
May 12 PHP
smarty内置函数section的用法
Jan 22 PHP
PHP中session跨子域的三种实现方法
Jul 25 PHP
PHP对XML内容进行修改和删除实例代码
Oct 26 PHP
PHP Include文件实例讲解
Feb 15 PHP
Laravel推荐使用的十个辅助函数
May 10 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
正义联盟的终局之战《天启星战争》将成为DC动画宇宙的最后一部
2020/04/09 欧美动漫
学习php设计模式 php实现桥梁模式(bridge)
2015/12/07 PHP
PHP数组的定义、初始化和数组元素的显示实现代码
2016/11/05 PHP
jQuery.ajax 用户登录验证代码
2010/10/29 Javascript
基于jquery的$.ajax async使用
2011/10/19 Javascript
poshytip 基于jquery的 插件 主要用于显示微博人的图像和鼠标提示等
2012/10/12 Javascript
关于JavaScript命名空间的一些心得
2014/06/07 Javascript
JavaScript判断数组是否存在key的简单实例
2016/08/03 Javascript
Node.js开发教程之基于OnceIO框架实现文件上传和验证功能
2016/11/30 Javascript
jQuery实现圣诞节礼物动画案例解析
2016/12/25 Javascript
详谈Angular路由与Nodejs路由的区别
2017/03/05 NodeJs
JavaScript+HTML5实现的日期比较功能示例
2017/07/12 Javascript
javascript代码优化的8点总结
2018/01/29 Javascript
express如何使用session与cookie的方法
2018/01/30 Javascript
Vue.js 实现微信公众号菜单编辑器功能(二)
2018/05/08 Javascript
springMvc 前端用json的方式向后台传递对象数组方法
2018/08/07 Javascript
vue引入微信sdk 实现分享朋友圈获取地理位置功能
2019/07/04 Javascript
Js通过AES加密后PHP用Openssl解密的方法
2019/07/12 Javascript
修改layui的后台模板的左侧导航栏可以伸缩的方法
2019/09/10 Javascript
关于vue组件事件属性穿透详解
2019/10/28 Javascript
vue 使用插槽分发内容操作示例【单个插槽、具名插槽、作用域插槽】
2020/03/06 Javascript
vue动态加载SVG文件并修改节点数据的操作代码
2020/08/17 Javascript
[51:32]Optic vs Serenity 2018国际邀请赛淘汰赛BO3 第一场 8.22
2018/08/23 DOTA
Python中字典的基础知识归纳小结
2015/08/19 Python
Python实现读取邮箱中的邮件功能示例【含文本及附件】
2017/08/05 Python
Python Socket使用实例
2017/12/18 Python
Pandas 重塑(stack)和轴向旋转(pivot)的实现
2019/07/22 Python
python实现双人五子棋(终端版)
2020/12/30 Python
网络优化专员求职信
2014/05/04 职场文书
司法建议书范文
2014/05/13 职场文书
摄影展策划方案
2014/06/02 职场文书
大学生在校表现评语
2014/12/31 职场文书
升职感谢信
2015/01/22 职场文书
2016年春季开学典礼新闻稿
2015/11/25 职场文书
python 如何用terminal输入参数
2021/05/25 Python
MySQL创建管理KEY分区
2022/04/13 MySQL