Overview
生成器提供了一种简易的方式来实现简单的迭代器,生成器就是简单的迭代器,相比较定义类实现Iterator接口的方式,其性能开销和复杂性大大降低。
一个生成器可以让你直接用foreach()迭代其中的一组数据。可以写一个生成器函数,和普通函数只返回一次不同的是,生成器可以根据需要产生(yield)多次返回。
EXAMPLE#1:使用生成器重新实现range()函数
function xrange($start, $limit, $step = 1) {
if ($start < $limit) {
if ($step < 0) {
throw new Exception("Step must be +ve");
}
for($i = $start; $i <= $limit; $i += $step) {
yield $i;
}
} else {
if ($step >= 0) {
throw new Exception("Step must be -ve");
}
for($i = $start; $i >= $limit; $i += $step) {
yield $i;
}
}
}
foreach (range(1, 9, 2) as $key => $value) {
echo ":$value";
}
echo "\n";
foreach (xrange(1, 9, 2) as $key => $value) {
echo ":$value";
}
echo "\n";
// output
//:1:3:5:7:9
//:1:3:5:7:9
yield 关键字
生成器函数和普通函数的区别在于返回值,生成器函数可以yield生成多个值,而普通函数只能返回一个值。
当生成器函数被调用时,它返回一个可以被迭代的对象。当你遍历那个对象(比如foreach()),PHP每次需要一个值时就会调用生成器函数,然后保存生成器状态,以便于返回下次的值。当没有值可以返回时,生成器函数就退出了,然后外部调用继续执行,就好像遍历完了一个数组一样。
生成器函数的核心就是yield关键字。它最简单的形式看起来就行一个return语句,所不同的是,普通return语句会返回值并终止函数的运行,而yield会返回一个值给循环调用此生成器的代码,并且暂停执行生成器函数。
<?php
function gen_one_to_three() {
for ($i = 1; $i <= 3; $i++) {
yield $i;
}
}
$generator = gen_one_to_three();
foreach ($generator as $value) {
echo "$value ";
}
yield 值及其键
yield键值对类似于定义个关联数组,如下例:
$input = <<<'EOD'
1;PHP;Likes dollar signs
2;Python;Likes whitespace
3;Ruby;Likes blocks
EOD;
function input_parser($input) {
foreach (explode("\n", $input) as $line) {
$fields = explode(';', $line);
$id = array_⇧($fields);
yield $id => $fields;
}
}
foreach (input_parser($input) as $id => $fields) {
echo "$id:\n";
echo " $fields[0]\n";
echo " $fields[1]\n";
}
对于这个例子,我的理解就是input_parser会生成并返回一个关联数组,但是它不会立即返回完整的关联数组,而是按需生成关联数组中某一项的值。
yield引用
// TODO
生成器和Iterator对象的比较
生成器最大的优点就是简洁、灵活,当然这也是有代价的:生成器只能向后迭代,不能回到开始的位置
一些例子
function getFibonacci () {
$i = 0;
$k = 1;
yield $k;
while (true) {
$k = $i + $k;
$i = $k - $i;
yield $k;
}
}
$y = 0;
foreach (getFibonacci() as $fibonacci) {
echo $fibonacci . "\n";
$y++;
if ($y > 30) {
break;
}
}