yield from
Ключевое слово yield from в PHP используется для делегирования генерации значений другому генератору, позволяя объединять несколько генераторов.
Введение
yield from — это выражение, позволяющее одному генератору делегировать работу другому итерируемому объекту: другому генератору, массиву или любому Traversable. Вместо того чтобы вручную перебирать внутренний источник и повторно выдавать каждый элемент, достаточно написать одну конструкцию yield from, и PHP самостоятельно передаст все значения (и ключи) вызывающему коду.
Это упрощает компоновку генераторов: вы можете строить большой поток значений из небольших, узкоспециализированных генераторных функций, не материализуя всю последовательность в памяти. Введённое в PHP 7.0, yield from является стандартным способом выравнивания вложенных генераторов.
В этой главе рассматриваются базовый синтаксис, обработка ключей, получение возвращаемого значения делегированного генератора и ситуации, когда делегирование является правильным инструментом.
Базовый синтаксис
yield from используется внутри генераторной функции и следует за любым итерируемым объектом:
yield from $iterable; // generator, array, or TraversableВот минимальный пример, объединяющий два генератора в один поток:
<?php
function myGenerator()
{
yield "Hello";
yield "World";
}
function myOtherGenerator()
{
yield "!";
}
function myCombinedGenerator()
{
yield from myGenerator();
yield from myOtherGenerator();
}
foreach (myCombinedGenerator() as $value) {
echo $value . " ";
}myCombinedGenerator() не производит собственных значений — он полностью делегирует работу двум другим генераторам. При его вызове сначала выдаются значения из myGenerator(), затем из myOtherGenerator(), поэтому foreach выводит:
Hello World !В одной функции можно свободно смешивать делегирование с обычными операторами yield:
<?php
function countToThree()
{
yield 1;
yield 2;
yield 3;
}
function countToFive()
{
yield from countToThree(); // delegates 1, 2, 3
yield 4; // then yields its own values
yield 5;
}
foreach (countToFive() as $value) {
echo $value . " ";
}
// Output: 1 2 3 4 5Делегирование массивам и другим итерируемым объектам
Источник после yield from не обязан быть генератором. Подойдёт любой массив или Traversable, что удобно для совместного выравнивания фиксированных и ленивых последовательностей:
<?php
function items()
{
yield from ['apple', 'banana']; // an array
yield from ['cherry'];
}
foreach (items() as $item) {
echo $item . "\n";
}
// Output:
// apple
// banana
// cherryКак обрабатываются ключи
yield from сохраняет ключи внутреннего итерируемого объекта, а не только значения. Это наиболее распространённая ловушка: ключи не перенумеровываются, поэтому два делегированных источника могут порождать дублирующиеся ключи.
<?php
function inner()
{
yield 'a' => 1;
yield 'b' => 2;
}
function outer()
{
yield from inner();
yield 'c' => 3;
}
foreach (outer() as $key => $value) {
echo "$key => $value\n";
}
// Output:
// a => 1
// b => 2
// c => 3Поскольку ключи сохраняются как есть, не следует рассчитывать на непрерывную последовательность 0, 1, 2, … при делегировании источникам с целочисленными ключами — если нужна переиндексация, соберите значения в массив с помощью iterator_to_array($gen, false).
Получение возвращаемого значения внутреннего генератора
Делегированный генератор может return финальное значение (отдельное от выдаваемых значений). Выражение yield from вычисляется как это возвращаемое значение, которое можно присвоить переменной:
<?php
function inner()
{
yield 1;
yield 2;
return 'done';
}
function outer()
{
$result = yield from inner();
echo "Inner returned: $result\n";
yield 3;
}
foreach (outer() as $value) {
echo "Value: $value\n";
}
// Output:
// Value: 1
// Value: 2
// Inner returned: done
// Value: 3Обратите внимание: возвращаемое значение внутреннего генератора захватывается конструкцией yield from — оно не передаётся в цикл foreach. До потребителя доходят только выданные значения (1, 2, 3).
Когда использовать yield from
Используйте yield from, когда вам нужно:
- Компоновать генераторы — строить конвейер из небольших, однозадачных генераторных функций вместо одной большой функции.
- Выравнивать вложенные структуры — рекурсивно обходить деревья или вложенные массивы и выдавать единый плоский поток значений.
- Сохранять эффективность памяти — значения проходят лениво, по одному, и вся последовательность никогда не держится в памяти.
В сравнении с ручным повторным выдаванием (foreach ($inner as $v) { yield $v; }), yield from короче, автоматически сохраняет ключи и передаёт возвращаемое значение внутреннего генератора — поэтому предпочтительно использовать именно его при делегировании другому итерируемому объекту.
Связанные темы
- yield — основа генераторов в PHP.
- Цикл foreach — способ потребления значений генератора.
- Функции PHP и return — строительные блоки, лежащие в основе генераторных функций.