PHP的学习--生成器Generators
生成器总览
(PHP 5 >= 5.5.0, PHP 7)
生成器提供了一种更容易的方法来实现简单的对象迭代,相比较定义类实现 Iterator 接口的方式,性能开销和复杂性大大降低。
生成器允许你在 foreach 代码块中写代码来迭代一组数据而不需要在内存中创建一个数组, 那会使你的内存达到上限,或者会占据可观的处理时间。相反,你可以写一个生成器函数,就像一个普通的自定义函数一样, 和普通函数只返回一次不同的是, 生成器可以根据需要 yield 多次,以便生成需要迭代的值。
一个简单的例子就是使用生成器来重新实现 range() 函数。 标准的 range() 函数需要在内存中生成一个数组包含每一个在它范围内的值,然后返回该数组, 结果就是会产生多个很大的数组。 比如,调用 range(0, 1000000) 将导致内存占用超过 100 MB。
做为一种替代方法, 我们可以实现一个 xrange() 生成器, 只需要足够的内存来创建 Iterator 对象并在内部跟踪生成器的当前状态,这样只需要不到1K字节的内存。
Example #1 将 range() 实现为生成器
<?php
function xrange($start, $limit, $step = 1) {
if ($start < $limit) {
if ($step <= 0) {
throw new LogicException('Step must be +ve');
} for ($i = $start; $i <= $limit; $i += $step) {
yield $i;
}
} else {
if ($step >= 0) {
throw new LogicException('Step must be -ve');
} for ($i = $start; $i >= $limit; $i += $step) {
yield $i;
}
}
} /*
* 注意下面range()和xrange()输出的结果是一样的。
*/ echo 'Single digit odd numbers from range(): ';
foreach (range(1, 9, 2) as $number) {
echo "$number ";
}
echo "\n"; echo 'Single digit odd numbers from xrange(): ';
foreach (xrange(1, 9, 2) as $number) {
echo "$number ";
}
?>
以上例程会输出:
Single digit odd numbers from range():
Single digit odd numbers from xrange():
Generator objects
When a generator function is called for the first time, an object of the internal Generator class is returned. This object implements the Iterator interface in much the same way as a forward-only iterator object would, and provides methods that can be called to manipulate the state of the generator, including sending values to and returning values from it.
生成器语法
一个生成器函数看起来像一个普通的函数,不同的是普通函数返回一个值,而一个生成器可以 yield 生成许多它所需要的值。
当一个生成器被调用的时候,它返回一个可以被遍历的对象.当你遍历这个对象的时候(例如通过一个foreach循环),PHP 将会在每次需要值的时候调用生成器函数,并在产生一个值之后保存生成器的状态,这样它就可以在需要产生下一个值的时候恢复调用状态。
一旦不再需要产生更多的值,生成器函数可以简单退出,而调用生成器的代码还可以继续执行,就像一个数组已经被遍历完了。
Note:
一个生成器不可以返回值: 这样做会产生一个编译错误。然而return空是一个有效的语法并且它将会终止生成器继续执行。
yield关键字
生成器函数的核心是yield关键字。它最简单的调用形式看起来像一个return申明,不同之处在于普通return会返回值并终止函数的执行,而yield会返回一个值给循环调用此生成器的代码并且只是暂停执行生成器函数。
Example #1 一个简单的生成值的例子
<?php
function gen_one_to_three() {
for ($i = 1; $i <= 3; $i++) {
//注意变量$i的值在不同的yield之间是保持传递的。
yield $i;
}
} $generator = gen_one_to_three();
foreach ($generator as $value) {
echo "$value\n";
}
?>
以上例程会输出:
Note:
在内部会为生成的值配对连续的整型索引,就像一个非关联的数组。
如果在一个表达式上下文(例如在一个赋值表达式的右侧)中使用yield,你必须使用圆括号把yield申明包围起来。 例如这样是有效的:
$data = (yield $value);
而这样就不合法,并且在PHP5中会产生一个编译错误:
$data = yield $value;
The parenthetical restrictions do not apply in PHP 7.
这个语法可以和生成器对象的Generator::send()方法配合使用。
指定键名来生成值
PHP的数组支持关联键值对数组,生成器也一样支持。所以除了生成简单的值,你也可以在生成值的时候指定键名。
如下所示,生成一个键值对与定义一个关联数组十分相似。
Example #2 生成一个键值对
<?php
/*
* 下面每一行是用分号分割的字段组合,第一个字段将被用作键名。
*/ $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";
}
?>
以上例程会输出:
:
PHP
Likes dollar signs
:
Python
Likes whitespace
:
Ruby
Likes blocks
和之前生成简单值类型一样,在一个表达式上下文中生成键值对也需要使用圆括号进行包围:
$data = (yield $key => $value);
生成null值
Yield可以在没有参数传入的情况下被调用来生成一个 NULL值并配对一个自动的键名。
Example #3 生成NULLs
<?php
function gen_three_nulls() {
foreach (range(1, 3) as $i) {
yield;
}
} var_dump(iterator_to_array(gen_three_nulls()));
?>
以上例程会输出:
array() {
[]=>
NULL
[]=>
NULL
[]=>
NULL
}
使用引用来生成值
生成函数可以像使用值一样来使用引用生成。这个和returning references from functions(从函数返回一个引用)一样:通过在函数名前面加一个引用符号。
Example #4 使用引用来生成值
<?php
function &gen_reference() {
$value = 3; while ($value > 0) {
yield $value;
}
} /*
* 我们可以在循环中修改$number的值,而生成器是使用的引用值来生成,所以gen_reference()内部的$value值也会跟着变化。
*/
foreach (gen_reference() as &$number) {
echo (--$number).'... ';
}
?>
以上例程会输出:
... ... ...
Generator delegation via yield from¶
In PHP 7, generator delegation allows you to yield values from another generator, Traversable object, or array by using the yield from keyword. The outer generator will then yield all values from the inner generator, object, or array until that is no longer valid, after which execution will continue in the outer generator.
If a generator is used with yield from, the yield from expression will also return any value returned by the inner generator.
Example #5 Basic use of yield from
<?php
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 ";
}
?>
以上例程会输出:
Example #6 yield from and return values
<?php
function count_to_ten() {
yield 1;
yield 2;
yield from [3, 4];
yield from new ArrayIterator([5, 6]);
yield from seven_eight();
return yield from nine_ten();
} function seven_eight() {
yield 7;
yield from eight();
} function eight() {
yield 8;
} function nine_ten() {
yield 9;
return 10;
} $gen = count_to_ten();
foreach ($gen as $num) {
echo "$num ";
}
echo $gen->getReturn();
?>
以上例程会输出:
Comparing generators with Iterator objects
The primary advantage of generators is their simplicity. Much less boilerplate code has to be written compared to implementing an Iterator class, and the code is generally much more readable. For example, the following function and class are equivalent:
<?php
function getLinesFromFile($fileName) {
if (!$fileHandle = fopen($fileName, 'r')) {
return;
} while (false !== $line = fgets($fileHandle)) {
yield $line;
} fclose($fileHandle);
} // versus... class LineIterator implements Iterator {
protected $fileHandle; protected $line;
protected $i; public function __construct($fileName) {
if (!$this->fileHandle = fopen($fileName, 'r')) {
throw new RuntimeException('Couldn\'t open file "' . $fileName . '"');
}
} public function rewind() {
fseek($this->fileHandle, 0);
$this->line = fgets($this->fileHandle);
$this->i = 0;
} public function valid() {
return false !== $this->line;
} public function current() {
return $this->line;
} public function key() {
return $this->i;
} public function next() {
if (false !== $this->line) {
$this->line = fgets($this->fileHandle);
$this->i++;
}
} public function __destruct() {
fclose($this->fileHandle);
}
}
?>
This flexibility does come at a cost, however: generators are forward-only iterators, and cannot be rewound once iteration has started. This also means that the same generator can't be iterated over multiple times: the generator will need to either be rebuilt by calling the generator function again, or cloned via the clone keyword.
PHP的学习--生成器Generators的更多相关文章
- PHP 生成器Generators的入门理解和学习
什么是生成器Generators 生成器允许你在 foreach 代码块中写代码来迭代一组数据而不需要在内存中创建一个数组, 那会使你的内存达到上限,或者会占据可观的处理时间.相反,你可以写一个生成器 ...
- 深入浅出ES6(十一):生成器 Generators,续篇
作者 Jason Orendorff github主页 https://github.com/jorendorff 欢迎回到深入浅出ES6专栏,望你在ES6探索之旅中收获知识与快乐!程序员们在工作 ...
- es6学习笔记二:生成器 Generators
今天这篇文章让我感到非常的兴奋,接下来我们将一起领略ES6中最具魔力的特性. 为什么说是“最具魔力的”?对于初学者来说,此特性与JS之前已有的特性截然不同,可能会觉得有点晦涩难懂.但是,从某种意义上来 ...
- 深入浅出ES6(三):生成器 Generators
作者 Jason Orendorff github主页 https://github.com/jorendorff ES6生成器(Generators)简介 什么是生成器? 我们从一个示例开始: ...
- PHP生成器Generators
下文的第一个逐行读取文件例子用三种方式实现;普通方法,迭代器和生成器,比较了他们的优缺点,很好,可以引用到自己的代码中 ,支持的php版本(PHP 5 >= 5.5.0) 后面的yield讲解, ...
- Python学习---生成器的学习1210
在Python中,这种一边循环一边计算的机制,称为生成器: 结论: 生成器本质是一个函数,不同于函数的是它生成的是一个对象,不执行函数内的代码 1.1. 列表生成器 列表生成器: 列表是直接生成数字在 ...
- Python学习-生成器 - Generator
简单来说,generator是一个能够返回迭代器对象的函数. yield的使用: 在python中,当你定义一个函数,使用了yield关键字时,这个函数就是一个生成器,它的执行会和其他普通的函数有很多 ...
- 生成器 Generators
function* quips(name) { yield "你好 " + name + "!"; yield "希望你能喜欢这篇介绍ES6的译文&q ...
- (转)python基础学习-----生成器和迭代器
在Python中,很多对象都是可以通过for语句来直接遍历的,例如list.string.dict等等,这些对象都可以被称为可迭代对象.至于说哪些对象是可以被迭代访问的,就要了解一下迭代器相关的知识了 ...
随机推荐
- HashMap LinkedHashMap源码分析笔记
MapClassDiagram
- (转)Android开发出来的APP在手机的安装路径是?
一.安装路径在哪? Android应用安装涉及到如下几个目录: system/app系统自带的应用程序,无法删除.data/app用户程序安装的目录,有删除权限.安装时把apk文件复制到此目录.dat ...
- mobaxterm ssh command
ssh -qTfnNg -D 7070 demouser@echo.supportedns.com -p 2233
- poj 1806 分块模拟
Manhattan 2025 Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 1318 Accepted: 703 Des ...
- 解剖SQLSERVER 第三篇 数据类型的实现(译)
解剖SQLSERVER 第三篇 数据类型的实现(译) http://improve.dk/implementing-data-types-in-orcamdf/ 实现对SQLSERVER数据类型的解 ...
- PCWIFI--无线网络共享软件
前段时间由于需要共享笔记本无线网络给手机使用,在网上找了几个软件试了一下,没找到比较好用的,要么是收费的,要么有广告,要么附带一大堆其他功能,所以决定自己写一个小软件来实现该功能.软件相关介绍如下: ...
- 在Ubuntu中安装Python3
首先,通过命令行安装Python3.2,只需要在终端中通过命令行安装即可: sudo apt-get install python3 一路yes. 因为Ubuntu很多底层采用的是Python2. ...
- 分布式系统之Quorum (NRW)算法
基于Quorum投票的冗余控制算法 Quorom 机制,是一种分布式系统中常用的,用来保证数据冗余和最终一致性的投票算法,其主要数学思想来源于鸽巢原理. 在有冗余数据的分布式存储系统当中,冗余数据对象 ...
- ECSHOP农行支付接口开发(含手机端)
对于ECSHOP来说,支付是以接口的形式存在的.于是: 1:首先添加接口文件 includes\modules\payment下,增加abcbank.php,代码如下: <?php /** * ...
- 移动APP的自动化测试
开发移动应用,最耗时耗力的就是手动测试APP的每个功能点或修复bug.有人就会提议App的业务逻辑可以使用nUnit或xUnit测试单元来辅助完成.那用户界面要如何测试?众所周知,移动设备多种多样,数 ...