文档章节

php Generator

n
 nic_chen
发布于 2017/01/19 15:13
字数 1597
阅读 18
收藏 1
php

yield特性一般跟生成器generator紧密联系在一起

Generator implements Iterator {

/* Methods */

public mixed current ( void )

public mixed key ( void )

public void next ( void )

public void rewind ( void )

public mixed send ( mixed $value )

public mixed throw ( Exception $exception )

public bool valid ( void )

public void __wakeup ( void )

}

注释:send()方法主要用于发送数据给当前yield,即yield被当作一个值被替换,且继续执行下一个yield,相当于next加current

所谓Generators, 我们以下称为”生成器”, 是一种可以返回迭代器的生成器. 呵呵, 这话有点绕, 让我们看看一个代码, 在没有迭代器之前, 如果我们遍历一个动态生成的数组:

<?php

   function return_array() {

      $array = dummy(); //计算全部数组内容

      return $array;

  }

foreach (return_array() as $v) {}

这里就有一个问题, 我们需要一次性生成全部数组内容, 并且返回, 想象一下如果数据来源非常大, 我们无法一次性读入内存.

当然, 我们可以采用一个类, 封装一个支持迭代的实现:

<?php

  class dummy implements Iterator {

       public function rewind() {

      //实现代码

      }

      public function valid() {

      //实现代码

      }

    public function current() {

      //实现代码

    }

    public function key() {

    //实现代码

    }

    public function next() {

    //实现代码

    }

}

foreach (new Dummy() as $v) {}

相比这种实现, 生成器提供了一种更加简便的选择, 比如实现如上同样的功能:

<?php

function genrators() {

   while ($i = dummy_line()) //生成数组的一个元素 

  {

          yield $i;

   }

}

foreach (generators() as $v) {}

也就是说, 每当产生一个数组元素, 就通过yield关键字返回成一个, 并且函数执行暂停, 当返回的迭代器的next方法被调用的时候, 会恢复刚才函数的执行, 从上一次被yield暂停的位置开始继续执行, 到下一次遇到yield的时候, 再次返回.

迭代生成器

(迭代)生成器也是一个函数,不同的是这个函数的返回值是依次返回,而不是只返回一个单独的值.或者,换句话说,生成器使你能更方便的实现了迭代器接口.下面通过实现一个xrange函数来简单说明:

<?php

function xrange($start, $end, $step = 1) {

    for ($i = $start; $i <= $end; $i += $step) {

        yield $i;

    }

}

foreach (xrange(1, 1000000) as $num) {

   echo $num, "\n";

}

上面这个xrange()函数提供了和PHP的内建函数range()一样的功能.但是不同的是range()函数返回的是一个包含值从1到100万0的数组(注:请查看手册). 而xrange()函数返回的是依次输出这些值的一个迭代器, 而不会真正以数组形式返回.

这种方法的优点是显而易见的.它可以让你在处理大数据集合的时候不用一次性的加载到内存中.甚至你可以处理无限大的数据流.

当然,也可以不同通过生成器来实现这个功能,而是可以通过继承Iterator接口实现.但通过使用生成器实现起来会更方便,不用再去实现iterator接口中的5个方法了.

生成器为可中断的函数

要从生成器认识协程, 理解它内部是如何工作是非常重要的: 生成器是一种可中断的函数, 在它里面的yield构成了中断点.

还是看上面的例子, 调用xrange(1,1000000)的时候, xrange()函数里代码其实并没有真正地运行. 它只是返回了一个迭代器:

<?php

$range = xrange(1, 1000000);

var_dump($range); // object(Generator)#1

var_dump($range instanceof Iterator); // bool(true)

?>

这也解释了为什么xrange叫做迭代生成器, 因为它返回一个迭代器, 而这个迭代器实现了Iterator接口.

调用迭代器的方法一次, 其中的代码运行一次.例如, 如果你调用$range->rewind(), 那么xrange()里的代码就会运行到控制流第一次出现yield的地方. 而函数内传递给yield语句的返回值可以通过$range->current()获取.

为了继续执行生成器中yield后的代码, 你就需要调用$range->next()方法. 这将再次启动生成器, 直到下一次yield语句出现. 因此,连续调用next()和current()方法, 你就能从生成器里获得所有的值, 直到再没有yield语句出现.

对xrange()来说, 这种情形出现在$i超过$end时. 在这中情况下, 控制流将到达函数的终点,因此将不执行任何代码.一旦这种情况发生,vaild()方法将返回假, 这时迭代结束.

协程

协程的支持是在迭代生成器的基础上, 增加了可以回送数据给生成器的功能(调用者发送数据给被调用的生成器函数). 这就把生成器到调用者的单向通信转变为两者之间的双向通信.

传递数据的功能是通过迭代器的send()方法实现的. 下面的logger()协程是这种通信如何运行的例子:

<?php

function logger($fileName) {

    $fileHandle = fopen($fileName, 'a');

    while (true) {

    fwrite($fileHandle, yield . "\n");

    }

}

$logger = logger(__DIR__ . '/log');

$logger->send('Foo');

$logger->send('Bar')

?>

正如你能看到,这儿yield没有作为一个语句来使用, 而是用作一个表达式, 即它能被演化成一个值. 这个值就是调用者传递给send()方法的值. 在这个例子里, yield表达式将首先被”Foo”替代写入Log, 然后被”Bar”替代写入Log.

上面的例子里演示了yield作为接受者, 接下来我们看如何同时进行接收和发送的例子:

<?php

function gen() {

    $ret = (yield 'yield1');

    var_dump($ret);

    $ret = (yield 'yield2');

    var_dump($ret);

}

$gen = gen();

var_dump($gen->current()); // string(6) "yield1"

var_dump($gen->send('ret1')); // string(4) "ret1" (the first var_dump in gen)

// string(6) "yield2" (the var_dump of the ->send() return value)

var_dump($gen->send('ret2')); // string(4) "ret2" (again from within gen)

// NULL (the return value of ->send())

?>

要很快的理解输出的精确顺序可能稍微有点困难, 但你确定要搞清楚为什按照这种方式输出. 以便后续继续阅读.

另外, 我要特别指出的有两点:

第一点,yield表达式两边的括号在PHP7以前不是可选的, 也就是说在PHP5.5和PHP5.6中圆括号是必须的.

第二点,你可能已经注意到调用current()之前没有调用rewind().这是因为生成迭代对象的时候已经隐含地执行了rewind操作.

本文转载自:http://www.cnblogs.com/hike2008/p/4556507.html

n
粉丝 1
博文 12
码字总数 7222
作品 0
成都
私信 提问
PostgreSQL PHP Generator 14.10 发布

PostgreSQL PHP Generator 14.10 发布,此版本现已提供下载:http://www.sqlmaestro.com/products/postgresql/phpgenerator/。在线演示:http://demo.sqlmaestro.com/ 重要的十大新特性 新增......

oschina
2014/11/06
1K
3
EasyRdf 0.3 发布

EasyRdf 是一个 PHP 库,用来快速简单的生成网站的RDF摘要信息。 该版本改进内容有: 1. Support for filtering of literals by language was added. 2. Parsers were moved into the EasyR...

绿悠悠
2009/12/22
75
0
MongoDB3.0集群配置文件自动生成器

请到此下载:项目Github地址 项目Github地址 https://github.com/jockchou/mongodb-conf-generator(https://github.com/jockchou/mongodb-conf-generator) cfg.conf.template : 集群配制服务......

jockchou
2015/07/16
214
0
Zend Framework Models Generator 3.0

Zend Framework Models Generator 3.0 发布了,该版本增加对 PostgreSQL 和 SQLite 数据库的支持。 Zend Framework Models Generator 为 Zend Framework 框架根据 MySQL 数据库来生成模型类。...

oschina
2013/04/27
846
0
Zend Framework Models Generator 3.1

Zend Framework Models Generator 3.1 修复了生成模型时使用 Primary key 和 Not null 的 bug。 Zend Framework Models Generator 为 Zend Framework 框架根据 MySQL 数据库来生成模型类。可......

oschina
2013/05/02
235
0

没有更多内容

加载失败,请刷新页面

加载更多

Flink Graph生成及Hash生成分析

222

MrPei
25分钟前
3
0
[译]Android Activity 和 Fragment 状态保存与恢复的最佳实践

https://blog.csdn.net/growing_tree/article/details/53759564 https://blog.csdn.net/u013588712/article/details/54691791...

shzwork
26分钟前
2
0
调用第三方快递鸟物流单号查询接口API代码示例

最近进行网站后台开发,需要实现物流的即时查询,发现之前集成的 快递100物流查询 API ——【PHP 快递查询源码资源】 已经不能正常使用了; 为了方便以后的业务需求,经过比较,最后选择使用...

程序的小猿
33分钟前
4
0
java Poi 操作执行excel 文件中函数问题

poi 读取excel 文件,当excel 有函数时,poi直接读取返回的是excel 函数,并不能返回函数计算结果: 解决步骤: sheet.setForceFormulaRecalculation(true); 判断该列格式是否为...

早a
41分钟前
5
0
js模拟实现输入框input事件

直接修改value值是无法触发对应元素的事件的。 通过发送输入框input事件了, 可以触发。 这里简单封装了一个方法. window.inputValue = function (dom, st) { var evt = new InputEvent('i...

開援带碼
42分钟前
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部