ジェネレータはPHP5.5で導入された機能です。PHP7では、ジェネレータをさらに便利にするような文法としてyield from
とreturn
が実装されました。これはPython 3.3から文法ごと輸入したものです(たぶん)。それぞれ簡単に説明します。
yield from
PHP5では、ジェネレータ関数が値を返す構文はyield
しかありませんでした。PHP7では新たな構文が導入されます。
yield from <expr>
この文では<expr>
から順に値を取り出し、それぞれの値をyield
します。<expr>
にはTraversable
なオブジェクトまたは配列を指定することができます。
<?php
function gen2($i) {
yield $i;
yield $i*2;
}
function gen1() {
yield from [1,2];
yield from gen2(4);
}
foreach (gen1() as $i) {
var_dump($i);
}
上記プログラムを実行すると下記のような結果が得られます。
int(1)
int(2)
int(4)
int(8)
これは「ジェネレータ関数の処理の一部を別のジェネレータに委譲する」などと説明されることも多いようです。巨大で複雑なジェネレータ関数を作るような場合に、この構文で簡単に関数分割ができますね。
ジェネレータ関数でのreturn
PHP5までのジェネレータ関数にはreturn
を書くことはできませんでした。PHP7からは、ジェネレータ関数にreturn
を書くことができるようになりました。return
のタイミングでジェネレータ関数は終了します。また、return
した値はジェネレータの新たなメソッドgetReturn()
で取得することができます。
<?php
function gen() {
yield 1;
yield 2;
return 3;
}
$gen = gen();
foreach ($gen as $i) {
var_dump($i);
}
echo $gen->getReturn(), "\n";
上記プログラムを実行すると下記のような結果が得られます。
int(1)
int(2)
3
この例だとreturnの存在理由がわからない程度ですが、yield from
文と組み合わせると少し違った動きになります。yield from
文でジェネレータを呼び出した場合、ジェネレータのreturn
値がyield from
文の評価値になります。
<?php
function main() {
$gen = gen1(0);
$x = $gen->current();
printf("%d <--yield\n", $x);
$y = $gen->send(2);
printf("%d <--yield\n", $y);
$z = $gen->send(5);
printf("%d <--yield\n", $z);
$w = $gen->send(7);
printf("%d <--yield\n", $w);
$gen->send(10);
printf("%d <--return\n", $gen->getReturn());
}
function gen1($a) {
printf("arg--> %d\n", $a);
$b = yield 1;
printf("send--> %d\n", $b);
$c = yield from gen2(3);
printf("%d <--return\n", $c);
$d = yield 9;
printf("send--> %d\n", $d);
return 11;
}
function gen2($a) {
printf("arg--> %d\n", $a);
$b = yield 4;
printf("send--> %d\n", $b);
$c = yield 6;
printf("send--> %d\n", $c);
return 8;
}
main();
上記プログラムを実行すると下記のような結果が得られます。
arg--> 0
1 <--yield
send--> 2
arg--> 3
4 <--yield
send--> 5
6 <--yield
send--> 7
8 <--return
9 <--yield
send--> 10
11 <--return
yield from
で呼び出されているジェネレータ関数gen2()
の返す値のうち、4と6はmain()
が受け取り、8はgen1()
が受け取っていることがわかります。このように、異なる2つの関数と値のやりとりができているのは特徴的だと言えます。
この例はかなり複雑なので、手元で試した方が理解が進むかもしれません。
感想など
将来的にはPythonのasyncioのようにyield from
を活用するライブラリが出てくると思いますが、それまでは謎の機能という扱いになりそうですね…。