19

我有一个方法,它需要一个生成器加上一些额外的参数并返回一个新的生成器:

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方法的正确方法是什么?

4

4 回答 4

24

有点晚了,但我自己需要一个空的生成器,并意识到创建一个实际上很容易......

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

不知道这是否比使用更好EmptyIterator,但这样你至少可以得到与非空生成器完全相同的类型。

于 2017-03-24T22:45:44.687 回答
4

只是为了完整起见,也许是迄今为止最不冗长的答案:

function generator() {
    return; yield;
}

我只是想知道同样的问题,并记得文档中的早期描述(至少在语义上直到今天),生成器函数是带有yield关键字的任何函数。

现在当函数在它产生之前返回时,生成器应该是空的。

就是这样。

3v4l.org 上的示例:https ://3v4l.org/iqaIY

于 2019-04-04T12:34:42.383 回答
3

我找到了解决方案:

由于\Generator扩展\Iterator,我可以将方法签名更改为:

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

这是输入协方差,因此它破坏向后兼容性,但前提是有人确实扩展了该merge方法。任何调用仍然有效。

现在我可以使用 PHP 的 native 调用该方法EmptyIterator

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

通常的生成器也可以工作:

merge(source(), [4, 5])
于 2014-08-22T11:40:25.973 回答
0

官方文档中所述,您可以通过在表达式中使用来创建内联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也可以正常工作。

于 2014-08-21T14:54:59.157 回答