ジェネレータのyield fromでネストを委譲して平坦化する
yield fromは、ジェネレータの中から別のジェネレータや配列の値をそのまま自分の出力として委譲する構文です。ネストしたyieldを平坦化して書けるPHPの機能ですね。
ネストしたジェネレータを手で回すのは面倒
ジェネレータの中から別のジェネレータの値を全部流したい、という場面があります。素朴に書くと、内側のジェネレータを foreach で回して一つずつ yield する形になりますが、これが何段も重なると読みづらくなりますね。
function inner() {
yield 1;
yield 2;
}
function outer() {
yield 0;
foreach (inner() as $v) { // 手で流し込む
yield $v;
}
yield 3;
}
yield fromで委譲する
yield from を使うと、別のジェネレータ(や配列、Traversable)の値をそのまま自分の出力として委譲できます。上の foreach が一行に置き換わって、意図もはっきりしますね。
function outer() {
yield 0;
yield from inner(); // 委譲
yield 3;
}
foreach (outer() as $v) {
echo $v; // 0, 1, 2, 3
}
配列も渡せるので、静的な並びと動的な生成を混ぜたいときにも便利です。yield from [10, 20]; のように書けます。
再帰的な走査と平坦化に効く
ツリー構造の走査など、再帰的にジェネレータを組み合わせるときに yield from は本領を発揮します。多次元配列を平坦に流す処理も、自分自身への委譲で素直に書けますね。
function flatten(array $items) {
foreach ($items as $item) {
if (is_array($item)) {
yield from flatten($item); // 自分に委譲
} else {
yield $item;
}
}
}
foreach (flatten([1, [2, [3, 4]], 5]) as $v) {
echo $v; // 1, 2, 3, 4, 5
}
まとめ
yield from はネストしたジェネレータの値を一段で流し込むための仕組みで、foreach + yield の定型を省けます。再帰的な走査や多次元の平坦化と相性がよく、メモリを食わずに巨大な構造を順に処理できるのも利点ですね。委譲先のジェネレータが return した値を受け取れる、という細かい機能もあるので、必要になったら調べてみるといいと思います。
よくある質問
Q. yield fromには配列も渡せますか。
A. 渡せます。配列やTraversableも委譲できるので、静的な並びと動的な生成を混ぜたいときにも使えます。
Q. yield fromはどんな処理と相性がいいですか。
A. ツリー構造の走査や多次元配列の平坦化など、再帰的にジェネレータを組み合わせる処理と相性がよいです。