php中Generator的执行过程

码途映星客
• 阅读 2757

说到php中的Generator(生成器),有人可能会想到协程,这里我们先不说php如何实现协程,我们探究下Generator的执行过程。
Generator是通过yield实现,yield 关键字是php5.5版本推出的一个特性。
首先,看下面的代码:

                function gen(){
                  while(true){
                    yield "gen\n";
                  }
                }
                
                $gen = gen();
                echo "Generator"; 
                

如果没有了解过yield的话,你会认为上面代码执行的结果是:死循环。但实际上,它会echo出Generator。

到这里,也许你会觉得奇怪,yield怎么可以结束循环?下面就为大家说明一下:

Generator提供的方法:

Generator::current — 返回当前产生的值
Generator::key — 返回当前产生的键
Generator::next — 生成器继续执行
Generator::rewind — 重置迭代器
Generator::send — 向生成器中传入一个值
Generator::throw — 向生成器中抛入一个异常
Generator::valid — 检查迭代器是否被关闭
Generator::__wakeup — 序列化回调

生成器提供了一种更容易的方法来实现简单的对象迭代(迭代器),相比较定义类实现 Iterator 接口的方式,性能开销和复杂性大大降低。

列子:

              function gen(){
                   for($i=0;$i<5;$i++)
                   {
                       echo (yield $i).$i.'<br/>';
                   }
            }
             $gen = gen();
             foreach($gen as $k=>$v){
                 echo "{$k}---{$v}".'<br/>';
             }

结果是:

php中Generator的执行过程

从上面的结果,我们可以分析出以下几点:
1当Generator对象被foreach的时候,内部的valid,current,key方法会依次被调用,其返回值是foreach语句的value和key。
2循环的终止条件则根据valid方法的返回而定。如果返回的是true则继续循环,如果是false则终止整个循环,结束遍历。
3一次循环体结束之后,将调用next进行下一次的循环直到valid返回false。而rewind方法则是在整个循环开始前被调用(也就是生成Generator对象时),这样保证了我们多次遍历得到的结果都是一致的。

下面我们来证明一下这个流程:

$gen = gen();
echo $gen->key();//结果是0,生成Generator对象时,rewind已经执行。
echo $gen->key().'----'.$gen->current();// 0----0
var_dump($gen->next());//var_dump值是null,但是还会echo出多一个0;这个0是怎样来的呢?原因是:next()执行后,第1个yield到第二个yieldz之间的的语法被执行,即是:echo (yield $i).$i.'<br/>';由于next()是没有返回值,即(yield $i)这个表达式没有值,而$i的值是0;
echo $gen->key().'----'.$gen->current();// 1----1 目前是第2个yield

上面这个例子可以证明,Generator内部的流程,特别注意next()的理解。

最后,我们说一下,send():

官方解析:向生成器中传入一个值,并且当做 yield 表达式的结果,然后继续执行生成器。如果当这个方法被调用时,生成器不在 yield 表达式,那么在传入值之前,它会先运行到第一个 yield 表达式。

翻译下的结论是:
send()方法主要用于发送数据给当前yield,即yield表达式被当作一个值被替换,且继续执行下一个yield,即next()

证明例子:
$gen = gen();
$gen->send(666);//6660

6660结果分析:首先把666代替当前yield表达式的值,然后执行next(),即运行echo (yield $i).$i.'<br/>',当前yield是666,所以最终结果是:6660。注意与next()的区别!!!

总结:
1.yield只能用于函数内部,在非函数内部运用会抛出错误。
2.如果函数包含了yield关键字的,那么函数执行后的返回值永远都是一个Generator对象。
3.如果函数内部同事包含yield和return 该函数的返回值依然是Generator对象,但是在生成Generator对象时,return语句后的代码被忽略。
4.Generator类实现了Iterator接口。
5.可以通过返回的Generator对象内部的方法,获取到函数内部yield后面表达式的值。
6.可以通过Generator的send方法给yield 关键字赋一个值。
7.一旦返回的Generator对象被遍历完成,便不能调用他的rewind方法来重置。
8.Generator对象不能被clone关键字克隆 。

实际应用:
1.协程
2.Genenrator返回的是迭代器,在处理大数据的时候不用一次性的加载到内存中,可看http://php.net/manual/zh/lang...

点赞
收藏
评论区
推荐文章
Python中的yield和generator
yeid可以把函数或者列表变成生成器(generator),如果只调用一部分结果,可以极大的缩减内存占用和增加运行速度,可以用next()或者循环得到生成器类型的数据。generator可以用()表示。deffab(max):n,a,b0,0,1whilen<max:yieldb
Stella981 Stella981
3年前
ES6中Generator理解
1\.生成器函数声明  function\ name(args){};2\.yield使用function hello(){    console.log('before hello');  //可看到hello()并不会立刻执行函数, 到第一次next调用时才会    var name 
Wesley13 Wesley13
3年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Wesley13 Wesley13
3年前
Go 并发
Go并发并发指的是同时处理多个任务的能力。并行指的是并行处理多个任务的能力。并行不一定加快运行速度,因为并行组件之间可能需要互相通信。Go中使用协程,信道来处理并发。协程Go中主要通过协程实现并发。协程是与其他函数或方法一起并发运行的函数或方法,协程可以看作是轻量级线程,但是创建成本更小,我们经常
Stella981 Stella981
3年前
Day11 python高级特性
直接可以作用于for循环的数据类型有以下几种:  • 集合数据类型:       list、tuple、dict、set、str  • Generator:       生成器和带yield的generatorfunction.这些可以直接作用于for循环的对象统称为可迭代对象:Iterable。可以通
Stella981 Stella981
3年前
ES6——Generator
ES6新引入了Generator函数,可以通过yield关键字,把函数的执行流挂起,为改变执行流程提供了可能,从而为异步编程提供解决方案。Generator函数组成Generator有两个区分于普通函数的部分一是在function后面,函数名之前有个\;函数内部有yield表达式。
Stella981 Stella981
3年前
Python 迭代器与生成器
python迭代器与生成器说到python迭代器,首先要明确两个概念:Iterable和Iterator,这两个概念还有Generator都是定义在collections模块里的。Iterable意为“可迭代的(对象)”,包括如下两种:1、实现了__getitem__(self,
Easter79 Easter79
3年前
Swoole2.0内置协程并发测试
Swoole2.0是一个革命性的版本,它内置了协程的支持。与Go语言协程不同,Swoole协程完全不需要开发者添加任何额外的关键词,直接以过去最传统的同步阻塞模式编写代码,底层自动进行协程调度实现异步IO。使并发编程变得非常简单。最新的版本中,内置协程已支持PHP7,同时兼具了性能和并发能力,Swoole的强大超乎想象。本文基于Github最新的Sw
Stella981 Stella981
3年前
PHP+jQuery寥寥几行代码轻松实现百度搜索那样的无刷新PJAX的分页列表和导航链接
!(https://static.oschina.net/uploads/space/2016/1208/171419_U00R_561214.png)PHP寥寥几行代码轻松实现百度搜索那样的分页列表和导航链接,某些语言的拥趸哭晕在厕所.<?php$apparray('db_prefix''
Stella981 Stella981
3年前
PHP 5.5 新特性关键字 yield
PHP5.5新特性关键字yield(https://www.oschina.net/action/GoToLink?urlhttp%3A%2F%2Fblog.lmlphp.com%2Farchives%2F126%2FPHP_5_5_new_features_yield)技术(https://www.oschina.net/action/Go
Stella981 Stella981
3年前
Python之yield语法
生成器与yield函数使用yield关键字可以定义生成器对象。生成器是一个函数。它生成一个值的序列,以便在迭代中使用,例如:1defcountdown(n):2print('倒计时:%s'%n)3whilen0:4yieldn5