Как получить пустой генератор?

У меня есть метод, который принимает генератор плюс некоторые дополнительные параметры и дает новый генератор:

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