Как выдать пустой генератор?

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

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]);

Что именно так, как я бы сделал в C# (есть IEnumerable<T>.Empty имущество). Но я не вижу никаких empty Генератор в руководстве.

Мне удалось обойти это (пока) с помощью этой функции:

function sourceEmpty()
{
    if ( false ) {
        yield;
    }
}

И это работает. Код:

foreach ( merge(sourceEmpty(), [4, 5]) as $item ) {
    var_dump($item);
}

правильно выводит:

int(4)
int(5)

Но это, очевидно, не идеальное решение. Каков будет правильный способ передачи пустого генератора в merge метод?

3 ответа

Решение

Я нашел решение:

поскольку \Generator продолжается \Iterator Я могу просто изменить подпись метода на это:

function merge(\Iterator $carry, array $additional) 
{
    // ...

Это входная ковариация, поэтому она нарушит обратную совместимость, но только если кто-то расширит merge метод. Любые вызовы все равно будут работать.

Теперь я могу вызвать метод с родным PHP EmtpyIterator:

merge(new \EmptyIterator, [4, 5]);

И обычный генератор тоже работает:

merge(source(), [4, 5])

Немного опоздал, но сам нуждался в пустом генераторе и понял, что создать его на самом деле довольно легко...

function empty_generator(): Generator
{
    yield from [];
}

Не знаю, лучше ли это, чем использовать EmptyIterator, но таким образом вы получите точно такой же тип, как и непустые генераторы, по крайней мере.

Просто для полноты, возможно, наименее подробный ответ до сих пор:

function generator() {
    return; yield;
}

Я только задавался вопросом об этом же вопросе и вспомнил раннее описание в документах (которое должно быть по крайней мере семантически до сегодняшнего дня), что функция генератора - это любая функция с yield ключевое слово.

Теперь, когда функция вернется до того, как выйдет, генератор должен быть пустым.

И так оно и есть.

Пример на 3v4l.org: https://3v4l.org/iqaIY

Как объясняется в официальных документах, вы можете создать Generator Например, с помощью yield в выражении:

$empty = (yield);

Это должно работать, но когда я попытался использовать это, я получил фатальную ошибку (yield выражение может быть использовано только в функции). С помощью null тоже не помогло:

$empty = (yield null); //error

Так что я думаю, что вы застряли с sourceEmpty функция... это было единственное, что я нашел, что работает... обратите внимание, что это создаст null значение в массиве, который вы перебираете.
Весь код был протестирован на PHP 5.5.9, кстати

Лучшее исправление, которое я могу придумать (учитывая, что совместимость является проблемой), было бы сделать оба аргумента необязательными:

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 тоже будет работать нормально.

Другие вопросы по тегам