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