生成器語法

生成器函數(shù)看起來像普通函數(shù)——不同的是普通函數(shù)返回一個值,而生成器可以 yield 生成多個想要的值。 任何包含 yield 的函數(shù)都是一個生成器函數(shù)。

當(dāng)一個生成器被調(diào)用的時候,它返回一個可以被遍歷的對象.當(dāng)你遍歷這個對象的時候(例如通過一個foreach循環(huán)),PHP 將會在每次需要值的時候調(diào)用對象的遍歷方法,并在產(chǎn)生一個值之后保存生成器的狀態(tài),這樣它就可以在需要產(chǎn)生下一個值的時候恢復(fù)調(diào)用狀態(tài)。

一旦不再需要產(chǎn)生更多的值,生成器可以簡單退出,而調(diào)用生成器的代碼還可以繼續(xù)執(zhí)行,就像一個數(shù)組已經(jīng)被遍歷完了。

注意:

生成器能夠返回多個值,通過 Generator::getReturn() 可以獲取到。

yield關(guān)鍵字

生成器函數(shù)的核心是yield關(guān)鍵字。它最簡單的調(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ù)的整型索引,就像一個非關(guān)聯(lián)的數(shù)組。

警告

傳入 Generator::send() 的值會被賦值到 $data, 或者直接調(diào)用 Generator::next() 時,賦的值將是 null。

指定鍵名來生成值

PHP的數(shù)組支持關(guān)聯(lián)鍵值對數(shù)組,生成器也一樣支持。所以除了生成簡單的值,你也可以在生成值的時候指定鍵名。

如下所示,生成一個鍵值對與定義一個關(guān)聯(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);

生成 null 值

Yield 可以在沒有參數(shù)傳入的情況下被調(diào)用來生成一個 null 值并配對一個自動的鍵名。

示例 #3 生成nulls

<?php
function gen_three_nulls() {
    foreach (
range(13) 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 生成器委托

生成器委托允許使用 yield from 關(guān)鍵字從另外一個生成器、 Traversable 對象、array 通過生成值。 外部生成器將從內(nèi)部生成器、object、array 中生成所有的值,直到它們不再有效, 之后將在外部生成器中繼續(xù)執(zhí)行。

如果生成器與 yield from 一起使用,那么 yield from 表達式將返回內(nèi)部生成器返回的任何值。

警告

存儲到 array (例如使用 iterator_to_array()

yield from 不能重置 key。它保留 Traversable 對象或者 array 返回的 key。因此,某些值可能會與其他的 yield 或者 yield from 共享公共的 key,因此,在插入數(shù)組時將會用這個 key 覆蓋以前的值。

一個非常重要的常見情況是 iterator_to_array() 默認返回帶 key 的 array , 這可能會造成無法預(yù)料的結(jié)果。 iterator_to_array() 還有第二個參數(shù) use_keys ,可以設(shè)置為 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 [
34];
    yield from new 
ArrayIterator([56]);
    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 [
34];
    yield from new 
ArrayIterator([56]);
    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