PHP5.5.0版本中,新增了生成器(Generators)特性,用于简化实现迭代器接口(Iterator)创建简单的迭代器的复杂性。

通过生成器,我们可以轻松的使用foreach迭代一系列的数据,而不需要事先在内存中构建要被迭代的对象,大大减少了内存开销。

当生成器函数被调用的时候,它会返回一个可迭代的对象,当对该对象进行迭代的时候,PHP将会在需要的时候调用生成器函数,并且在生成器使用新增的关键字yield产生一个新的值的时候,保存迭代器内部的状态。迭代器没有新的值需要产生的时候,生成器函数就可以直接退出,外部函数继续执行。

注意,在生成器函数中,不能使用return语句返回值,使用return返回值的话会产生编译器错误。但是,使用空的return是可以的,它会使迭代器终止。

生成器函数与普通函数一样的,唯一的区别函数内使用了yield关键字。yield语句可以说是生成器函数的核心,简单来说,yield就像return语句一样,区别是return语句返回后函数就结束了,而使用yield返回后,只是暂停了函数的执行,转到外部函数继续执行,下次调用生成器函数的时候,继续执行生成器函数内部的代码。

一个简单的例子 - 生成器版本的range函数

一个简单的例子是使用foreach迭代函数range的返回值,如果调用的是range(0, 1000000)的话,将会消耗超过100M的内存。而使用生成器的话,可能只需要消耗1KB内存都不到。

<?php
function xrange($start, $end) {
if ($start > $end) {
throw new RuntimeException("起始值不能大于截止值");
}
for ($i = $start; $i <= $end; $i += 1) {
// 使用yield关键字,每次到这里函数都会返回$i的值,并且控制权交给外部函数继续执行
yield $i;
}
} foreach (xrange(1, 9) as $number) {
echo "$number ";
}

上面的例子输出如下:

上述例子中,我们创建了一个名为xrange的函数,函数中使用yield不断产生返回值,而调用xrange(1, 9)将会创建一个生成器对象。我们可以修改foreach这一行打印出xrange对象看看

...
$xrange_res = xrange(1, 9);
var_dump($xrange_res);
foreach( $xrange_res as $number){
...

输出

可以看出,执行xrange(1, 9)的时候确实是返回了一个Generator对象。

使用Generator对象的send方法

在上面的例子中,我们使用yield语句的时候都是作为单独的一行语句执行的,也就是yield语句产生结果给外部,那么在迭代过程中有没有办法从生成器函数外部获取值呢?

办法总是有的,因为调用生成器函数后返回的是一个Generator对象,因此我们可以通过调用该对象的send方法从外部给生成器函数传递一个值,在调用send方法之后,yield会收到send函数发送的值。

<?php
function gen() {
$ret = (yield 'yield1');
var_dump("-->" . $ret); $ret = (yield 'yield2');
var_dump("-->" . $ret);
} $gen = gen(); var_dump($gen->current());
var_dump($gen->send('ret1'));
var_dump($gen->send('ret2'));

输出:

这里我们首先创建了名为gen的生成器对象,然后打印$gen->current()方法的返回值,该返回值就是迭代器第一次迭代时产生的当前值,因此输出了yield1

接下来我们调用了$gen->send('ret')方法,这时,生成器内第一个yield语句返回该方法传递的值ret1,因此输出了$ret的值为ret1

接着由于生成器内部执行到了第三条语句$ret = (yield 'yield2'),因此外部的第二个var_dump输出了yield2。最后调用$gen->send('ret2')与第一次类似,不过这次生成器内部调用yield之后已经没有yield了,因此返回的是NULL

注意,这里的$ret = (yield 'yield2')语句中,使用括号包含了yield 'yield2'语句,这里是必须的,如果在表达式上下文中使用yield,必须将yield放在括号内,否则会报错。

返回关联数组

前面的例子中,我们使用yield关键字返回的总是单个值,实际上PHP也对返回关联数组提供了支持,基本语法:

yield key => val

使用该语法格式可以在foreach的时候,返回与遍历管理数组相同的结果。

<?php
function gen2() {
$array = [
'username' => 'mylxsw',
'site' => 'http://aicode.cc'
]; foreach ($array as $key => $val) {
yield $key => $val;
}
} foreach(gen2() as $key => $val) {
var_dump($key . ' : ' . $val);
}

输出:

使用引用

我们还可以让生成器以引用的方式返回数据,这样就可以在生成器外部直接修改生成器内部数据的值。

<?php
function &gen_reference() {
$value = 3; while ($value > 0) {
yield $value;
}
} foreach (gen_reference() as &$number) {
echo (--$number).'... ';
}

上述例子中,需要注意的是,生成器函数的定义和遍历的时候使用了&$number

最后,生成器与自定义的迭代器对象是不完全相同的,生成器一旦开始迭代,就不能再rewind了,只能一直向前迭代,直到迭代完成。如果希望多次迭代一个生成器对象的话,可以多次调用生成器函数创建新的生成器对象或者是使用clone关键字。


参考:

Php5.5新特性 Generators详解的更多相关文章

  1. JDK19新特性使用详解

    前提 JDK19于2022-09-20发布GA版本,本文将会详细介绍JDK19新特性的使用. 新特性列表 新特性列表如下: JPE-405:Record模式(预览功能) JPE-422:JDK移植到L ...

  2. HTML5新特性及详解

    什么是HTML5:HTML5 是下一代的HTML,将成为 HTML.XHTML 以及 HTML DOM 的新标准. 为 HTML5 建立的一些规则: 新特性应该基于 HTML.CSS.DOM 以及 J ...

  3. iOS7新特性-NSURLSession详解

    前言:本文由DevDiv版主@jas 原创翻译,转载请注明出处!原文:http://www.shinobicontrols.com/b ... day-1-nsurlsession/ 大家都知道,过去 ...

  4. Java8新特性--lamada详解

    最近玩了一下这个,感觉挺有趣的,语法使用起来很简洁,让代码看起来挺清爽易读的. 看了一下源码,发现挺充分的利用了jak1.5的特性(注解.泛型). 但是,具体的实现流程还是有点不通透,先Mark,等用 ...

  5. Servlet3.0新特性使用详解

    可插拔的Web框架 几乎所有基于Java的web框架都建立在servlet之上.现今大多数web框架要么通过servlet.要么通过Web.xml插入.利用标注(Annotation)来定义servl ...

  6. Java8新特性: CompletableFuture详解

    CompletableFuture实现了CompletionStage接口和Future接口,前者是对后者的一个扩展,增加了异步回调.流式处理.多个Future组合处理的能力,使Java在处理多任务的 ...

  7. ios新特征 ARC详解

    IOS ARC 分类: IOS ARC2013-01-17 09:16 2069人阅读 评论(0) 收藏 举报   目录(?)[+]   关闭工程的ARC(Automatic Reference Co ...

  8. php5.3新特性 之 mysql native driver(mysqlnd)

    概述 本文主要写给sa看的.码农就不用看了. mysql native driver(mysqlnd) 自从php5.3.0开始成为官方源代码的一部分, 用来取代传统的mysql client lib ...

  9. PHP5.3, PHP5.4, PHP5.5新特性

    PHP 5.3中的新特性 1. 支持命名空间 (Namespace) 2. 支持延迟静态绑定(Late Static Binding) 3. 支持goto语句 4. 支持闭包.Lambda/Anony ...

随机推荐

  1. 微信小程序,开发中几个重要的知识点(加密解密,转发,进入场景,session_key)

    小程序的授权信息:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/authorize.html 小程序的 ...

  2. 设计模式-装饰模式(Decorator Pattern)

    装饰模式(Decorator Pattern):动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活

  3. VS2015中运行ASPX老项目出错HTTP Error 500.23 - Internal Server Error错误

    今天翻出以前用VS2010做的老项目,在VS2015中运行ASPX页面浏览,出现错误: HTTP Error 500.23 - Internal Server Error 检测到在集成的托管管道模式下 ...

  4. Oracle Time Model Statistics(时间模型统计)

    Oracle数据库从10g開始,启用以时间模型统计为主.命中率为辅等性能度量指标.基于时间模型统计,主要是基于操作类型測量在数据库中花费的时间的统计信息.最重要的时间模型统计是数据库时间.或DB时间. ...

  5. elasticsearch 索引延迟 一致性问题等

    https://www.elastic.co/guide/en/elasticsearch/guide/current/near-real-time.html https://www.elastic. ...

  6. spark 分区

    http://stackoverflow.com/questions/39368516/number-of-partitions-of-spark-dataframe

  7. WebSocket通信协议 API简介

    WebSocket是html5新增加的一种通信协议,目前流行的浏览器都支持这个协议,例如 Chrome,Safari,Firefox,Opera,IE等等,对该协议支持最早的应该是chrome,从ch ...

  8. opencv项目报错_pFirstBlock==pHead解决办法

    备注: 我上次遇到这个问题的原因是项目设置为MTd导致的 OpenCV是MTd的,我要是改成MDd就编译报错,所以不能采用把项目改为MDd的办法,只能把OpenCV重新编译为MDd的,下载CMAKE, ...

  9. LeetCode: Palindrome Partitioning II 解题报告

    Palindrome Partitioning II Given a string s, partition s such that every substring of the partition ...

  10. debian下创建新用户useradd

    1.使用sudo: sudo useradd -m abc -g sudo -s /bin/bash -d /home/abc sudo passwd abc 2.直接在root用户下: groupa ...