ジェネレータの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. ツリー構造の走査や多次元配列の平坦化など、再帰的にジェネレータを組み合わせる処理と相性がよいです。

類似投稿

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です