生成器語(yǔ)法

生成器函數(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() 可以獲取到。

yield關(guān)鍵字

生成器函數(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。

指定鍵名來(lái)生成值

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)單值類型一樣,在一個(gè)表達(dá)式上下文中生成鍵值對(duì)也需要使用圓括號(hào)進(jìn)行包圍:

$data = (yield $key => $value);

生成 null 值

Yield 可以在沒(méi)有參數(shù)傳入的情況下被調(diào)用來(lái)生成一個(gè) null 值并配對(duì)一個(gè)自動(dòng)的鍵名。

示例 #3 生成nulls

<?php
function gen_three_nulls() {
    foreach (
range(13) as $i) {
        yield;
    }
}

var_dump(iterator_to_array(gen_three_nulls()));
?>

以上例程會(huì)輸出:

array(3) {
  [0]=>
  NULL
  [1]=>
  NULL
  [2]=>
  NULL
}

使用引用來(lái)生成值

生成函數(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 生成器委托

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

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

警告

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

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 [
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 ";
}
?>

以上例程會(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 [
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();
?>

以上例程會(huì)輸出:

1 2 3 4 5 6 7 8 9 10