LoginSignup
3
0

More than 5 years have passed since last update.

PHP: Generatorの関数合成

Last updated at Posted at 2019-02-26

本稿ではGeneratorを関数合成してひとつのGeneratorを作る方法を紹介する。

関数合成関数を実装する方法は、「PHP: 関数合成する関数を作る方法 - Qiita」で紹介した。ここで紹介した関数合成関数を使えば、Generatorも合成することができる。

どんな合成をしたいか?

3つのジェネレーターがある。これを合成して、ひとつのジェネレーターを作りたい。

function generator(string $name): callable
{
    return function (iterable $items) use ($name): iterable {
        foreach ($items as $item) {
            echo "generator('$name') yields $item\n";
            yield $item;
        }
    };
}

// 3つのジェネレーター
$a = generator('a');
$b = generator('b');
$c = generator('c');

手続き型で書くとこんな感じ:

$composedGenerator = function (iterable $items): iterable {
    $a = generator('a');
    $b = generator('b');
    $c = generator('c');
    yield from $c($b($a($items)));
};

foreach ($composedGenerator([1, 2, 3]) as $item) {
    echo "item = $item\n";
}

これを実行すると、次のような出力が出る:

generator('a') yields 1
generator('b') yields 1
generator('c') yields 1
item = 1
generator('a') yields 2
generator('b') yields 2
generator('c') yields 2
item = 2
generator('a') yields 3
generator('b') yields 3
generator('c') yields 3
item = 3

関数合成関数で合成する

関数合成関数はこれを使う:

$composeAll = function (callable $function, callable ...$functions): callable {
    return array_reduce(
        $functions,
        function (callable $f, callable $g): callable {
            return function (...$arguments) use ($f, $g) {
                return $g($f(...$arguments));
            };
        },
        $function
    );
};

合成のしかた:

$composedGenerator = $composeAll(generator('x'), generator('y'), generator('z'));

実行してみる:

foreach ($composedGenerator([1, 2, 3]) as $item) {
    echo "item = $item\n";
}

実行結果:

generator('x') yields 1
generator('y') yields 1
generator('z') yields 1
item = 1
generator('x') yields 2
generator('y') yields 2
generator('z') yields 2
item = 2
generator('x') yields 3
generator('y') yields 3
generator('z') yields 3
item = 3

合成できた。

別解

foreachする方法もある。

function compose(callable ...$functions): callable
{
    return function (iterable $items) use ($functions): iterable {
        foreach ($functions as $function) {
            $items = $function($items);
        }
        return $items;
    };
}

$composedGenerator = compose($a, $b, $c);

foreach ($composedGenerator([1, 2, 3]) as $item) {
    echo "item = $item\n";
}
3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0