How to yield empty generator?

…衆ロ難τιáo~ 提交于 2019-12-19 05:17:28

问题


I have a method which takes a generator plus some additional parameters and yields a new generator:

function merge(\Generator $carry, array $additional)
{
    foreach ( $carry as $item ) {
        yield $item;
    }
    foreach ( $additional as $item ) {
        yield $item;
    }
}

The usual use case for this function is similar to this:

function source()
{
    for ( $i = 0; $i < 3; $i++ ) {
        yield $i;
    }
}

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

But the problem is that sometimes I need to pass empty source to the merge method. Ideally I would like to be able to do something like this:

merge(\Generator::getEmpty(), [4, 5]);

Which is exactly how I would do in C# (there is a IEnumerable<T>.Empty property). But I don't see any kind of empty generator in the manual.

I've managed to work around this (for now) by using this function:

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

And this works. The code:

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

correctly outputs:

int(4)
int(5)

But this is obviously not an ideal solution. What would be the proper way of passing an empty generator to the merge method?


回答1:


Bit late, but needed an empty generator myself, and realized creating one is actually quite easy...

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

Don't know if that's better than using the EmptyIterator, but this way you get exactly the same type as non-empty generators at least.




回答2:


I've found the solution:

Since \Generator extends \Iterator I can just change the method signature to this:

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

This is input covariance thus it would break backward compatibility, but only if someone did extend the merge method. Any invocations will still work.

Now I can invoke the method with PHP's native EmtpyIterator:

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

And the usual generator also works:

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



回答3:


Just for completeness, perhaps the least verbose answer so far:

function generator() {
    return; yield;
}

I just wondered about the same question and remembered an early description in the docs (which should be in at least semantically until today) that a generator function is any function with the yield keyword.

Now when the function returns before it yields, the generator should be empty.

And so it is.

Example on 3v4l.org: https://3v4l.org/iqaIY




回答4:


As explained in the official docs, you can create an in-line Generator instance, by using yield in an expression:

$empty = (yield);

That should work, but when I tried using that, I got a fatal error (yield expression can only be used in a function). Using null didn't help either:

$empty = (yield null); //error

So I guess you're stuck with the sourceEmpty function... it was the only thing I found that works... note that it will create a null value in the array you're iterating.
All the code was tested on PHP 5.5.9, BTW

The best fix I can come up with (seeing as compatibility is an issue) would be to make both arguments optional:

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

This way, existing code won't brake, and instead of constructing an empty generator, passing null will work just fine, too.



来源:https://stackoverflow.com/questions/25428615/how-to-yield-empty-generator

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!