当前位置: 首页 > news >正文

Php5.5新特性 Generators详解

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

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 ";
}

上面的例子输出如下:

generator-output

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

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

输出

generator-vardump

可以看出,执行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'));

输出:

generator-gen

这里我们首先创建了名为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);
}

输出:

gen2-output

####使用引用

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

<?php
function &gen_reference() {
    $value = 3;

    while ($value > 0) {
        yield $value;
    }
}

foreach (gen_reference() as &$number) {
    echo (--$number).'... ';
}

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

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


参考:

  • Cooperative multitasking using coroutines (in PHP!)
  • Generators

转载于:https://my.oschina.net/agiledev/blog/424988

相关文章:

  • js修改后没反应-看看是不是取的缓存
  • 新时代的“网盘模型”——够快云库
  • ADT下载地址整理(转)
  • IntelliJ IDEA 使用心得与常用快捷键
  • asp.net Session 一般设置
  • iOS UITableView的Section Footer添加按钮
  • 南邮算法分析和实验设计1 分而治之
  • OpenStack对象存储——Swift
  • PHP生成缩略图、加水印
  • C语言中的单引号和双引号含义
  • 《Java从入门到精通》第七章学习笔记
  • 给Python用virtualenvwrapper
  • nginx.conf 完整的集群配置
  • Linux下查看文件和文件夹大小 df,du命令
  • UITableViewCell单元格的删除、插入、移动
  • [ 一起学React系列 -- 8 ] React中的文件上传
  • [分享]iOS开发 - 实现UITableView Plain SectionView和table不停留一起滑动
  • 2017前端实习生面试总结
  • 2018一半小结一波
  • CSS3 聊天气泡框以及 inherit、currentColor 关键字
  • Docker入门(二) - Dockerfile
  • Hexo+码云+git快速搭建免费的静态Blog
  • HTTP传输编码增加了传输量,只为解决这一个问题 | 实用 HTTP
  • IE报vuex requires a Promise polyfill in this browser问题解决
  • iOS动画编程-View动画[ 1 ] 基础View动画
  • leetcode讲解--894. All Possible Full Binary Trees
  • maven工程打包jar以及java jar命令的classpath使用
  • OpenStack安装流程(juno版)- 添加网络服务(neutron)- controller节点
  • 闭包--闭包作用之保存(一)
  • 基于Android乐音识别(2)
  • 技术胖1-4季视频复习— (看视频笔记)
  • 三分钟教你同步 Visual Studio Code 设置
  • 详解NodeJs流之一
  • 在GitHub多个账号上使用不同的SSH的配置方法
  • Python 之网络式编程
  • #Lua:Lua调用C++生成的DLL库
  • $().each和$.each的区别
  • (3)nginx 配置(nginx.conf)
  • (Java岗)秋招打卡!一本学历拿下美团、阿里、快手、米哈游offer
  • (二)hibernate配置管理
  • (附源码)ssm高校志愿者服务系统 毕业设计 011648
  • (附源码)ssm基于jsp的在线点餐系统 毕业设计 111016
  • (六)vue-router+UI组件库
  • (转)C#开发微信门户及应用(1)--开始使用微信接口
  • (转)LINQ之路
  • (轉貼) VS2005 快捷键 (初級) (.NET) (Visual Studio)
  • ***php进行支付宝开发中return_url和notify_url的区别分析
  • . NET自动找可写目录
  • .\OBJ\test1.axf: Error: L6230W: Ignoring --entry command. Cannot find argumen 'Reset_Handler'
  • .NET / MSBuild 扩展编译时什么时候用 BeforeTargets / AfterTargets 什么时候用 DependsOnTargets?
  • .NET 2.0中新增的一些TryGet,TryParse等方法
  • .NET Core 将实体类转换为 SQL(ORM 映射)
  • .net core控制台应用程序初识
  • .NET Micro Framework初体验
  • .net快速开发框架源码分享