本稿では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";
}