Как получить пустой генератор?
У меня есть метод, который принимает генератор плюс некоторые дополнительные параметры и дает новый генератор:
function merge(\Generator $carry, array $additional)
{
foreach ( $carry as $item ) {
yield $item;
}
foreach ( $additional as $item ) {
yield $item;
}
}
Обычный пример использования для этой функции аналогичен этому:
function source()
{
for ( $i = 0; $i < 3; $i++ ) {
yield $i;
}
}
foreach ( merge(source(), [4, 5]) as $item ) {
var_dump($item);
}
Но проблема в том, что иногда мне нужно передать пустой источник методу merge
. В идеале я хотел бы сделать что-то вроде этого:
merge(\Generator::getEmpty(), [4, 5]);
Именно так я и сделал бы на С# (есть свойство IEnumerable<T>.Empty
). Но я не вижу генератора empty
в руководстве.
Мне удалось обойти это (пока) с помощью этой функции:
function sourceEmpty()
{
if ( false ) {
yield;
}
}
И это работает. Код:
foreach ( merge(sourceEmpty(), [4, 5]) as $item ) {
var_dump($item);
}
правильно выводит:
int(4)
int(5)
Но это, очевидно, не идеальное решение. Каким будет правильный способ передачи пустого генератора методу merge
?
Ответы
Ответ 1
Я нашел решение:
Так как \Generator
extends \Iterator
, я могу просто изменить сигнатуру метода на это:
function merge(\Iterator $carry, array $additional)
{
// ...
Это ковариация ввода, поэтому она нарушит обратную совместимость, но только если кто-то расширил метод merge
. Любые вызовы будут по-прежнему работать.
Теперь я могу вызвать метод с родным PHP EmtpyIterator
:
merge(new \EmptyIterator, [4, 5]);
И обычный генератор также работает:
merge(source(), [4, 5])
Ответ 2
Бит опоздал, но мне нужен был пустой генератор, и реализовать его на самом деле довольно просто...
function empty_generator(): Generator
{
yield from [];
}
Не знаю, лучше ли это с помощью EmptyIterator
, но таким образом вы получите ровно тот же тип, что и непустые генераторы.
Ответ 3
Как объяснено в официальных документах, вы можете создать экземпляр Generator
в строке, используя yield
в выражении:
$empty = (yield);
Это должно работать, но когда я попытался использовать это, я получил фатальную ошибку (выражение yield
может использоваться только в функции). Использование null
не помогло:
$empty = (yield null); //error
Итак, я думаю, вы застряли в функции sourceEmpty
... это единственное, что я нашел, что работает... заметьте, что он создаст значение null
в массиве, который вы повторяете. < ш > Весь код был протестирован на PHP 5.5.9, BTW
Лучшее исправление, которое я могу придумать (поскольку проблема совместимости является проблемой), заключалась бы в том, чтобы оба аргумента были опциональными:
function merge(\Generator $carry = null, array $additional = array())
{
if ($carry)
foreach ($carry as $item)
yield $item;
foreach ($additional as $item)
yield $item;
}
foreach(merge(null, [1,2]) as $item)
var_dump($item);
Таким образом, существующий код не будет тормозить, и вместо построения пустого генератора, передача null
тоже будет очень хорошей.
Ответ 4
Просто для полноты, возможно, наименее подробный ответ до сих пор:
function generator() {
return; yield;
}
Я только задавался вопросом об этом же вопросе и вспомнил раннее описание в документах (которое должно быть по крайней мере семантически до сегодняшнего дня), что функцией генератора является любая функция с ключевым словом yield
.
Теперь, когда функция вернется до того, как выйдет, генератор должен быть пустым.
И так оно и есть.
Пример на 3v4l.org: https://3v4l.org/iqaIY