来源:https://segmentfault.com/a/1190000018457194

其实,我并不是因为迭代或者生成器或者研究PHP手册才认识的yield,要不是协程,我到现在也不知道PHP中还有yield这么个鬼东西。人家这个东西是从PHP 5.5就开始引入了,官方名称叫做生成器。你要说为什么5.5年代的东西,现在才拿出来。我还想问你哟,PHP 5.3就有了的namespace为毛到最近这几年才开始正式投产。

那么,问题来了,这东西到底是有何用?

先来感受一个问题,给你100Kb的内存(是的,你没有看错,就是100Kb),然后让你迭代输出一个从1开始一直到10000的数组,步进为1。

愈先迭代数组,必先创造数组。

所以,脑门一拍,代码一坨如下:

<?php
$start_mem = memory_get_usage();
$arr = range( 1, 10000 );
foreach( $arr as $item ){
//echo $item.',';
}
$end_mem = memory_get_usage();
echo " use mem : ". ( $end_mem - $start_mem ) .'bytes'.PHP_EOL;

一顿操作猛如虎,运行一下成绩1-5,你们感受一下:

528440bytes,约莫就是528Kb,几乎是100Kb的五倍了,妈的这日子没法过了。

毕竟你们也知道,最近内存价格确实贵,国家也在号召低碳节能减排,你多耗费5倍内存,就意味着多排放5倍的二氧化碳,就意味着要为多用的内存多花钱贡献给棒子... ...你想想,那可是棒子。

人都是被逼出来的,于是yield可以来救场了,大概代码如下,注意看操作:

<?php
$start_mem = memory_get_usage();
function yield_range( $start, $end ){
while( $start <= $end ){
$start++;
yield $start;
}
}
foreach( yield_range( 0, 9999 ) as $item ){
echo $item.',';
}
$end_mem = memory_get_usage();
echo " use mem : ". ( $end_mem - $start_mem ) .'bytes'.PHP_EOL;

运行一下,你们感受一下:

首先,我们观察一下yield_range这个函数跟普通函数不一样的地方,就是普通函数往往都是使用return来返回结果,而这个中则是yield。其次是普通函数中return只能返回一次,这个yield能返回好多次。

那么,我们来分析一波儿这个神奇的yield_range函数。这个yield关键字到底返回的是什么?我们简单看一下:

<?php
function yield_range( $start, $end ){
while( $start <= $end ){
$start++;
yield $start;
}
}
$rs = yield_range( 1, 100 );
var_dump( $rs );
/*
object(Generator)#1 (0) {
}
*/

yield返回的是一个叫做Generator(中文名就是生成器)的object对象,而这个生成器是实现了Iterator接口(至于Iterator接口,你们去PHP手册上搜索吧)。所以,既然实现了Iterator接口(也正是因为如此,这个东西可以使用foreach进行迭代,明白了吧?),所以可以有如下代码:

<?php
function yield_range( $start, $end ){
while( $start <= $end ){
yield $start;
$start++;
}
}
$generator = yield_range( 1, 10 );
// valid() current() next() 都是Iterator接口中的方法
while( $generator->valid() ){
echo $generator->current().PHP_EOL;
$generator->next();
}

运行结果如下所示:

重点来了:这个yield_range函数似乎能够记住它上一次运行到哪儿了,上一次运行的结果是什么,然后紧接着在下一次运行的时候继续从上次终止的地方继续开始。这不是普通的PHP函数可以做得到的!

我们知道,操作系统在调度进程的时候,会触发一个叫做“进程上下文切换”的概念。比如CPU从进程A调度给进程B了,那么当再次从进程B调度给进程A的时候,当初进程A运行到哪儿了、临时的数据结果是什么都是需要被还原的,不然,一切都要从头,那就要出大问题了。而,这个yield关键字,似乎在用户态(非系统内核级)就可以实现这个概念。所以说,用yield搞迭代,怕是真的很没出息的一件事,它能做的太多。

紧接着,我们需要认识一个生成器对象的一个方法,叫做send,简单看下下面这坨代码:

<?php
function yield_range( $start, $end ){
while( $start <= $end ){
$ret = yield $start;
$start++;
echo "yield receive : ".$ret.PHP_EOL;
}
}
$generator = yield_range( 1, 10 );
$generator->send( $generator->current() * 10 );

运行结果如图所示:

send方法可以修改yield的返回值,但是,你也不能想当然,比如下面这坨代码,你们以为运行结果是什么样呢?

<?php
function yield_range( $start, $end ){
while( $start <= $end ){
$ret = yield $start;
$start++;
echo "yield receive : ".$ret.PHP_EOL;
}
}
$generator = yield_range( 1, 10 );
foreach( $generator as $item ){
$generator->send( $generator->current() * 10 );
}

本来以为运行结果是类似于这样的:

<?php
yield receive : 10
yield receive : 20
yield receive : 30
yield receive : 40
yield receive : 50
yield receive : 60
yield receive : 70
yield receive : 80
yield receive : 90
yield receive : 100

然而,唯物主义告诉我们:

结果是打脸的,你们感受一下:

原因是什么呢?原因是当你在外部向yield发送send的时候,会自动触发一次next,自己动手试下吧。

PHP的yield是个什么玩意的更多相关文章

  1. 神马玩意,EntityFramework Core 1.1又更新了?走,赶紧去围观

    前言 哦,不搞SQL了么,当然会继续,周末会继续更新,估计写完还得几十篇,但是我会坚持把SQL更新完毕,绝不会烂尾,后续很长一段时间没更新的话,不要想我,那说明我是学习新的技能去了,那就是学习英语,本 ...

  2. Python 生成器与迭代器 yield 案例分析

    前几天刚开始看 Python ,后因为项目突然到来,导致Python的学习搁置了几天.然后今天看回Python 发现 Yield 这个忽然想不起是干嘛用的了(所以,好记性不如烂笔头.).然后只能 花点 ...

  3. node 异步回调解决方法之yield

    先看如何使用 使用的npm包为genny,npm 安装genny,使用 node -harmony 文件(-harmony 为使用es6属性启动参数) 启动项目 var genny= require( ...

  4. yield生成器及字符串的格式化

    一.生成器 def ran(): print('Hello world') yield 'F1' print('Hey there!') yield 'F2' print('goodbye') yie ...

  5. Python中的生成器与yield

    对于python中的yield有些疑惑,然后在StackOverflow上看到了一篇回答,所以搬运过来了,英文好的直接看原文吧. 可迭代对象 当你创建一个列表的时候,你可以一个接一个地读取其中的项.一 ...

  6. Python yield函数理解

    Python中的yield函数的作用就相当于一个挂起,是不被写入内存的,相当于一个挂起的状态,用的时候迭代,不用的时候就是一个挂起状态,挂起状态会以生成器的状态表现

  7. ecma6 yield

    function * generator(k){ console.log('begin'); var x = yield k; console.log('x:',x); var y = yield x ...

  8. Python yield与实现

    Python yield与实现  yield的功能类似于return,但是不同之处在于它返回的是生成器. 生成器 生成器是通过一个或多个yield表达式构成的函数,每一个生成器都是一个迭代器(但是迭 ...

  9. 可惜Java中没有yield return

    项目中一个消息推送需求,推送的用户数几百万,用户清单很简单就是一个txt文件,是由hadoop计算出来的.格式大概如下: uid caller 123456 12345678901 789101 12 ...

随机推荐

  1. SQL 实习

    一. tem表 group by 分组后,有三个编号,找出最下的订单时间,比较选出时间段内的数据.注意group by 和 having的用法. group by 的用法 不用聚合函数的时候,每类出现 ...

  2. 如何编写优雅的异步代码 — CompletableFuture

    前言 在我们的意识里,同步执行的程序都比较符合人们的思维方式,而异步的东西通常都不好处理.在异步计算的情况下,以回调表示的动作往往会分散在代码中,也可能相互嵌套在内部,如果需要处理其中一个步骤中可能发 ...

  3. vue组件中的style scoped中遇到的问题

    在uve组件中我们我们经常需要给style添加scoped来使得当前样式只作用于当前组件的节点.添加scoped之后,实际上vue在背后做的工作是将当前组件的节点添加一个像data-v-1233这样唯 ...

  4. cxk不会二进制 (贪心)

    cxk不会二进制 Description 最近cxk迷上了二进制,他很菜,有道简单的题不会做,挂在这里求大佬做一下: 以二进制形式给出两个数字:x,y.令s = x + y * 2 ^ k.输出能使 ...

  5. 深入解读ES6系列(三)

    ES6字符串 哈喽小伙伴们,爱说'废'话的Z又回来了,欢迎来到Super IT曾的博客时间,上一节说了函数,解构赋值和数组的五大将,这一节我们继续我们知识的海洋,一起奋斗不秃头!不足的欢迎提问留言. ...

  6. dict字典的用法

    在用dict遇到了一些困难,记一下. 代码1: books={"倚天屠龙记":{"id":1,"price":100}, "好吗好 ...

  7. python函数的传参模式

    python里的变量更像是一个名字.标签.而Python中一切又皆为对象. 当函数传参时,函数参数作为一个标签,指向某个对象,因此更贴切的说是"call by object". 但 ...

  8. reuire代码优化之:r.js

    r.js是requireJS的优化(Optimizer)工具,可以实现前端文件的压缩与合并,在requireJS异步按需加载的基础上进一步提供前端优化,减小前端文件大小.减少对服务器的文件请求.要使用 ...

  9. MyBatis(七):使用注解替代xml文件

    本文是按照狂神说的教学视频学习的笔记,强力推荐,教学深入浅出一遍就懂!b站搜索狂神说或点击下面链接 https://space.bilibili.com/95256449?spm_id_from=33 ...

  10. 按公式产生随机数、java中的重载、递归、有关计算机计算的问题

    1.按公式产生随机数x1=(16807*x)%(Integer.MAX_VALUE)x=x1;通过这个公式进行随机数的产生,当产生的数字大于2e+32-2时,在用产生随机数的方式进行数字的输出.主要思 ...