生成器函數(shù)看起來(lái)像普通函數(shù)——不同的是普通函數(shù)返回一個(gè)值,而生成器可以 yield 生成多個(gè)想要的值。 任何包含 yield 的函數(shù)都是一個(gè)生成器函數(shù)。
當(dāng)一個(gè)生成器被調(diào)用的時(shí)候,它返回一個(gè)可以被遍歷的對(duì)象.當(dāng)你遍歷這個(gè)對(duì)象的時(shí)候(例如通過(guò)一個(gè)foreach循環(huán)),PHP 將會(huì)在每次需要值的時(shí)候調(diào)用對(duì)象的遍歷方法,并在產(chǎn)生一個(gè)值之后保存生成器的狀態(tài),這樣它就可以在需要產(chǎn)生下一個(gè)值的時(shí)候恢復(fù)調(diào)用狀態(tài)。
一旦不再需要產(chǎn)生更多的值,生成器可以簡(jiǎn)單退出,而調(diào)用生成器的代碼還可以繼續(xù)執(zhí)行,就像一個(gè)數(shù)組已經(jīng)被遍歷完了。
注意:
生成器能夠返回多個(gè)值,通過(guò) Generator::getReturn() 可以獲取到。
生成器函數(shù)的核心是yield關(guān)鍵字。它最簡(jiǎn)單的調(diào)用形式看起來(lái)像一個(gè)return申明,不同之處在于普通return會(huì)返回值并終止函數(shù)的執(zhí)行,而yield會(huì)返回一個(gè)值給循環(huán)調(diào)用此生成器的代碼并且只是暫停執(zhí)行生成器函數(shù)。
示例 #1 一個(gè)簡(jiǎn)單的生成值的例子
<?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";
}
?>
以上例程會(huì)輸出:
1 2 3
注意:
在內(nèi)部會(huì)為生成的值配對(duì)連續(xù)的整型索引,就像一個(gè)非關(guān)聯(lián)的數(shù)組。
傳入 Generator::send() 的值會(huì)被賦值到 $data,
或者直接調(diào)用 Generator::next() 時(shí),賦的值將是 null
。
PHP的數(shù)組支持關(guān)聯(lián)鍵值對(duì)數(shù)組,生成器也一樣支持。所以除了生成簡(jiǎn)單的值,你也可以在生成值的時(shí)候指定鍵名。
如下所示,生成一個(gè)鍵值對(duì)與定義一個(gè)關(guān)聯(lián)數(shù)組十分相似。
示例 #2 生成一個(gè)鍵值對(duì)
<?php
/*
* 下面每一行是用分號(hào)分割的字段組合,第一個(gè)字段將被用作鍵名。
*/
$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";
}
?>
以上例程會(huì)輸出:
1: PHP Likes dollar signs 2: Python Likes whitespace 3: Ruby Likes blocks
和之前生成簡(jiǎn)單值類(lèi)型一樣,在一個(gè)表達(dá)式上下文中生成鍵值對(duì)也需要使用圓括號(hào)進(jìn)行包圍:
$data = (yield $key => $value);
Yield 可以在沒(méi)有參數(shù)傳入的情況下被調(diào)用來(lái)生成一個(gè) null
值并配對(duì)一個(gè)自動(dòng)的鍵名。
示例 #3 生成null
s
<?php
function gen_three_nulls() {
foreach (range(1, 3) as $i) {
yield;
}
}
var_dump(iterator_to_array(gen_three_nulls()));
?>
以上例程會(huì)輸出:
array(3) { [0]=> NULL [1]=> NULL [2]=> NULL }
生成函數(shù)可以像使用值一樣來(lái)使用引用生成。這個(gè)和從函數(shù)返回一個(gè)引用一樣:通過(guò)在函數(shù)名前面加一個(gè)引用符號(hào)。
示例 #4 使用引用來(lái)生成值
<?php
function &gen_reference() {
$value = 3;
while ($value > 0) {
yield $value;
}
}
/*
* 我們可以在循環(huán)中修改 $number 的值,而生成器是使用的引用值來(lái)生成,所以 gen_reference() 內(nèi)部的 $value 值也會(huì)跟著變化。
*/
foreach (gen_reference() as &$number) {
echo (--$number).'... ';
}
?>
以上例程會(huì)輸出:
2... 1... 0...
生成器委托允許使用 yield from 關(guān)鍵字從另外一個(gè)生成器、 Traversable 對(duì)象、array 通過(guò)生成值。 外部生成器將從內(nèi)部生成器、object、array 中生成所有的值,直到它們不再有效, 之后將在外部生成器中繼續(xù)執(zhí)行。
如果生成器與 yield from 一起使用,那么 yield from 表達(dá)式將返回內(nèi)部生成器返回的任何值。
yield from 不能重置 key。它保留 Traversable 對(duì)象或者 array 返回的 key。因此,某些值可能會(huì)與其他的 yield 或者 yield from 共享公共的 key,因此,在插入數(shù)組時(shí)將會(huì)用這個(gè) key 覆蓋以前的值。
一個(gè)非常重要的常見(jiàn)情況是 iterator_to_array() 默認(rèn)返回帶 key 的 array ,
這可能會(huì)造成無(wú)法預(yù)料的結(jié)果。 iterator_to_array() 還有第二個(gè)參數(shù)
use_keys
,可以設(shè)置為 false
來(lái)收集 Generator
返回的不帶 key 的所有值。
示例 #5 使用 iterator_to_array() 的 yield from
<?php
function inner() {
yield 1; // key 0
yield 2; // key 1
yield 3; // key 2
}
function gen() {
yield 0; // key 0
yield from inner(); // keys 0-2
yield 4; // key 1
}
// 傳遞 false 作為第二個(gè)參數(shù)獲得數(shù)組 [0, 1, 2, 3, 4]
var_dump(iterator_to_array(gen()));
?>
以上例程會(huì)輸出:
array(3) { [0]=> int(1) [1]=> int(4) [2]=> int(3) }
示例 #6 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 ";
}
?>
以上例程會(huì)輸出:
1 2 3 4 5 6 7 8 9 10
示例 #7 yield from 并返回多個(gè)值
<?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();
?>
以上例程會(huì)輸出:
1 2 3 4 5 6 7 8 9 10