问题
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