Php 如何传入空的生成器参数?

Php 如何传入空的生成器参数?,php,language-design,yield,language-details,Php,Language Design,Yield,Language Details,我有一个方法,它接受一个生成器加上一些附加参数,然后返回一个新的生成器: function merge(\Generator $carry, array $additional) { foreach ( $carry as $item ) { yield $item; } foreach ( $additional as $item ) { yield $item; } } 此功能的常用用例与此类似: function sourc

我有一个方法,它接受一个生成器加上一些附加参数,然后返回一个新的生成器:

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);
}
这正是我在C#中所做的(有一个
IEnumerable.Empty
属性)。但是我没有看到任何类型的
空的
生成器

我已通过使用此功能(目前)解决了此问题:

function sourceEmpty()
{
    if ( false ) {
        yield;
    }
}
这是有效的。守则:

foreach ( merge(sourceEmpty(), [4, 5]) as $item ) {
    var_dump($item);
}
正确输出:

int(4)
int(5)
但这显然不是一个理想的解决方案。将空生成器传递给
merge
方法的正确方法是什么?

如中所述,您可以通过在表达式中使用
yield
来创建一个内嵌
生成器
实例:

$empty = (yield);
这应该行得通,但当我尝试使用它时,我遇到了一个致命错误(
yield
expression只能在函数中使用)。使用
null
也没有帮助:

$empty = (yield null); //error
所以我猜您一直在使用
sourceEmpty
函数。。。这是我发现的唯一有效的东西。。。请注意,它将在正在迭代的数组中创建一个
null
值。
顺便说一句,所有代码都在PHP5.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
也可以正常工作,而不是构造一个空的生成器。

我找到了解决方案:

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

function merge(\Iterator $carry, array $additional) 
{
    // ...
这是输入协方差,因此它会破坏向后兼容性,但前提是有人扩展了
merge
方法。任何调用都将仍然有效

现在,我可以使用PHP的本机
EmptyIterator
调用该方法:

merge(new \EmptyIterator, [4, 5]);
通常的发电机也可以工作:

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

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

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

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

为了完整起见,也许是迄今为止最不详细的答案:

function generator() {
    return; yield;
}
我只是想知道同样的问题,还记得文档中的一个早期描述(到今天为止,至少在语义上应该是这样),即生成器函数是任何带有
yield
关键字的函数

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

事实就是这样


3v4l.org上的示例:

如果生成器为空,为什么要使用
合并
?为什么不直接使用foreach数组呢?无论如何,一个简单的解决方法是交换参数(首先放置数组),并将
生成器
参数设置为默认值
null
,使其成为可选值,或者只使用空的
数组()generator@EliasVanOotegem无法更改
merge
方法的API。它是更大的递归数组reduce算法的一部分,因此它必须与PHP的
array\u reduce
函数保持兼容。除此之外,其他代码已经使用了此方法,因此这将是向后兼容性中断。@Sirac删除
\Generator
类型提示(如果这是您建议的-否则
数组
将不起作用)是一种肮脏的攻击。我想保持干净。如果有人对性能影响感兴趣,下面是这个问题答案的VLD操作码:我没有分析和调用函数10000000次,因为函数调用开销差异很可能高于函数之间的实际差异,但似乎
false&&yield
具有最简单的操作码(甚至比
if(false)yield;
)更简单。实际上,我在提问之前测试了您提供的内联表达式,但正如您所说,它不起作用。但是,
sourceEmpty
的行为与您描述的不同。输出是正确的(至少对我来说,我是在5.5.15上)-没有额外的前面空值。这就是为什么它是迄今为止唯一有效的解决方案。可能是在我们版本之间的某个地方修复了一个bug。@MaciejSz:在变更日志中找不到任何东西表明这样的bug已经修复。不过,
迭代器
接口实现和生成器都有一些小改动。无论哪种方式,使这两个参数都是可选的(尽管很粗糙),这将是最接近无函数调用的解决方案。生成器在PHP中是非常新的,就像许多PHP最新的小玩意儿一样,它们不是真的…呃。。。这个概念的最佳实现。感谢您的投入,如果您有兴趣,我已经找到了一个更干净的解决方案。不过,关于PHP小饰品的实现,您完全正确:)包含
yield
的表达式不创建内联生成器,而是用于检索发送到生成器的值。我无法从您引用的PHP文档中找到确切的段落。您可以引用吗?您可以进一步将类型提示扩展到
Traversable
Iterator
、ArrayAccess
和其他一些接口都是从内部可遍历的
接口扩展而来的。为了完整性(但我想您已经知道这一点),暗示行为接口确实会使代码更容易出错。顺便说一句:如果它是一个全局对象,而您不在任何特定的名称空间中,则可以省略前导的反斜杠。我只需删除所有类型提示,并将两个参数记录为
@param\Traversable | array
。允许与任何可以迭代的“东西”一起使用。所以可以说
函数合并(iterable$carry,iterable$additional)
。这也是一个很好的解决方案。在我的例子中,我将坚持使用
new\EmptyIterator
,因为它不需要实现样板文件
function generator() {
    return; yield;
}