【modernPHP专题(7)】生成器语法

虚树潮涌
• 阅读 1500

一般你在迭代一组数据的时候,需要创建一个数据,假设数组很大,则会消耗很大性能,甚至造成内存不足。

//Fatal error: Allowed memory size of 1073741824 bytes exhausted (tried to allocate 32 bytes) 
range(1, 100000000);

PHP5.5实现了生成器,每当产生一个数组元素则用yield关键词返回,并且执行函数暂停,当执行函数next方法时,则会从上一次被yield的位置开始继续执行,如下例子,只会产生中间变量$i,并只在每次循环的赋值;

function xrange($start, $limit, $step = 1) {
    for ($i = $start; $i <= $limit; $i += $step) {
     //注意变量$i的值在不同的yield之间是保持传递的。
        yield $i;
    }
}

$generator = xrange(1, 100000000, 1);

// 可以通过foreach获得;
foreach ($generator as $number) {
   echo  "$number";
   echo PHP_EOL;
}

// 由于生成器其实是一个实现了iterator接口的类,所以也可以通过相关的iterator方法来迭代
// var_dump($generator)  class Generator#1 (0) {}
// Generator implements Iterator {}

$generator->rewind();
while ($generator->valid()){
    echo $generator->current();
    echo PHP_EOL;
    $generator->next();
}

生成器函数的核心是yield关键字。它最简单的调用形式看起来像一个return申明,不同之处在于普通return会返回值并终止函数的执行,而yield会返回一个值给循环调用此生成器的代码并且只是暂停执行生成器函数。

通过生成器来生成关联数组

/*
 * 下面每一行是用分号分割的字段组合,第一个字段将被用作键名。
 */
$input = <<<'EOF'
1;PHP;Likes dollar signs
2;Python;Likes whitespace
3;Ruby;Likes blocks
EOF;

function input_parser($input) {
    foreach (explode("\n", $input) as $line) {
        $fields = explode(';', $line);
        $id = array_shift($fields);
        yield $id => $fields;
    }
}

foreach (input_parser($input) as $id => $fields) {
    echo "$id:\n";
    echo "    $fields[0]\n";
    echo "    $fields[1]\n";
}
/*
    1:
        PHP
        Likes dollar signs
    2:
        Python
        Likes whitespace
    3:
        Ruby
        Likes blocks
 */

生成NULL值

略;

使用引用来生成值

先了解一下从函数返回一个引用的概念

手册解释: 引用返回用在当想用函数找到引用应该被绑定在哪一个变量上面时。不要用返回引用来增加性能,引擎足够聪明来自己进行优化。仅在有合理的技术原因时才返回引用!要返回引用,使用此语法:

Example: 使用返回引用

class foo{
    public $value = 42;

    public function &getValue()
    {
        return $this->value;
    }
}

$obj = new foo;
// $myValue is a reference to $obj->value, which is 42.
// $myValue 是 $obj->value 的引用,它们的值都是 42
$myValue = &$obj->getValue();
// 对 $obj->value 重新赋值,会影响到 $myValue 的值
$obj->value = 2;
// prints the new value of $obj->value, i.e. 2.
echo $myValue;    // 程序输出 2

Example: 没有使用返回引用

class foo {
    public $value = 42;

    public function getValue() {
        return $this->value;
    }
}

$obj = new foo;
$myValue = $obj->getValue();
$obj->value = 2;

echo $obj->value;  // 输出 2
echo $myValue;     //  输出42, 因为返回的是当时值的一个副本;

函数 &getValue() 把引用绑定在成员变量 $value 上了。正常来说,$obj = new foo; 产生的 $obj 是一个copy,它的成员变量 $value 与函数 getValue() 不存在“别名”(引用)关系。

Example: 通过引用来生成值

function &gen_reference() {
    $value = 3;
    while ($value > 0) {
        yield $value;
    }
}

/*
 * 我们可以在循环中修改$number的值,而生成器是使用的引用值来生成,所以gen_reference()内部的$value值也会跟着变化。
 */
foreach (gen_reference() as &$number) {
    echo (--$number).'...';//改变的是gen_reference()里面的$value值;这样里面的循环就不是死循环了。
}

yield from关键字

在PHP7的版本,生成器允许从其他生成器,可迭代对象或数组通过yield from关键字来生成对应的值;

function count_to_ten() {
    yield 1;
    yield 2;
    yield from [3, 4];
    yield from new ArrayIterator([5, 6]);
    yield from seven_eight();
    yield 9;
    yield 10;
}

function seven_eight() {
    yield 7;
    yield from eight();
}

function eight() {
    yield 8;
}

foreach (count_to_ten() as $num) {
    echo "$num ";
}

// 输出1 2 3 4 5 6 7 8 9 10 
点赞
收藏
评论区
推荐文章
美凌格栋栋酱 美凌格栋栋酱
7个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
凝雪探世界 凝雪探世界
4年前
超详细的 JS 数组方法
数组是js中最常用到的数据集合,其内置的方法有很多,熟练掌握这些方法,可以有效的提高我们的工作效率,同时对我们的代码质量也是有很大影响。一、创建数组1.使用数组字面量表示法var arr4  ;   //创建一个空数组var arr5  20;   // 创建一个包含1项数据为20的数组var arr
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
3年前
java中比较两个时间的差值
项目背景1.某篇文稿的发布时间是publishDate,例如:2020072118:00:41。2.现要求判断该篇文稿的发布时间是否在近30天之内。publicstaticlongdayDiff(DatecurrentDate,DatepublishDate){LongcurrentTimecurrentDat
Jacquelyn38 Jacquelyn38
4年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Easter79 Easter79
3年前
Systemd 三部曲 之 PHP7
导读在鸟哥的博客里看到了他们做的PHP的性能测试,相对于PHP5.6都是有很大的提升的,并且PHP的主版本号已经是2004年发布PHP5后,11年来首次更新,肯定PHP7是有很大的改变的,并且支持了Systemd。安装编译php7时需要的依赖包:yumyinstalllibxml2libxml2developen
Stella981 Stella981
3年前
Php5.5新特性 Generators详解
在PHP5.5.0版本中,新增了生成器\(Generators)_特性,用于简化实现迭代器接口_(Iterator)\创建简单的迭代器的复杂性。通过生成器,我们可以轻松的使用foreach迭代一系列的数据,而不需要事先在内存中构建要被迭代的对象,大大减少了内存开销。当生成器函数被调用的时候,它会返回一个可迭代的对象,当对该对象进行迭代
Wesley13 Wesley13
3年前
MongoDB分片介绍
本文简单介绍MongoDB的分片功能,对分片进行了概述,具体的功能详解,后续文章会陆续推出分片是把数据分配到多个服务器上的一种方式,MongoDB使用分片实现大数据部署以及高吞吐操作。大数据以及高吞吐量的应用会对单个服务器的容量造成很大的挑战。比如,高频率的查询操作会消耗服务器的CPU,如果数据集大于系统的RAM容量,也会对硬盘的性能造成影响。应
Stella981 Stella981
3年前
PHP 生成器Generators的入门理解和学习
什么是生成器Generators生成器允许你在foreach代码块中写代码来迭代一组数据而不需要在内存中创建一个数组,那会使你的内存达到上限,或者会占据可观的处理时间。相反,你可以写一个生成器函数,就像一个普通的自定义函数一样,和普通函数只返回一次不同的是,生成器可以根据需要yield多次,以便生成需要迭代的值。一个简单的例子就
Stella981 Stella981
3年前
Sphinx实时索引
数据库中的数据很大,然后我有些新的数据后来加入到数据库中,也希望能够检索到,全部重新建立索引很消耗资源,这样需要用到“主索引增量索引”的思路来解决,这个模式实现的基本原理是设置两个数据源和两个索引。1、创建一个计数器一个简单的实现是,在数据库中增加一个计数表,记录将文档集分为两个部分的文档ID,每次重新构建主索引时,更新这个表先在mysql
MySQL性能优化浅析及线上案例
业务发展初期,数据库中量一般都不高,也不太容易出一些性能问题或者出的问题也不大,但是当数据库的量级达到一定规模之后,如果缺失有效的预警、监控、处理等手段则会对用户的使用体验造成影响,严重的则会直接导致订单、金额直接受损,因而就需要时刻关注数据库的性能问题。