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;
}