Here's a simple JavaScript generator (via: http://blog.carbonfive.com/2013/12/01/hanging-up-on-callbacks-generators-in-ecmascript-6/)
function* powGenerator() {
var result = Math.pow(yield "a", yield "b");
return result;
}
var g = powGenerator();
console.log(g.next().value); // "a", from the first yield
console.log(g.next(10).value); // "b", from the second
console.log(g.next(2).value); // 100, the result
I'm trying to model something similar with PHP but it's a bit of a headache.
<?php
function powGenerator() {
return pow((yield 'a'), (yield 'b'));
}
Before I go further, I get this error in PHP
Fatal error: Generators cannot return values using "return"
Ok, so maybe I'll just use another yield to get the final value out? ...
<?php
function powGenerator() {
yield pow((yield 'a'), (yield 'b'));
}
$g = powGenerator(); //=> Generator {#180}
echo $g->send(10); //=> "b"
echo $g->send(2); //=> 100
OK, so I got my value back, but there's two major issues here
Where did my "a"
go? — Notice in the JS example I was able to access the both the "a"
and the "b"
yielded values as well as the 100
final result.
The generator is still not done! — I have to call send
an additional time to complete the generator
$g->valid(); //=> true
$g->send('?'); //=> null
$g->valid(); //=> false
From PHP Generator::send
public mixed Generator::send ( mixed $value )
Sends the given value to the generator as the result of the current
yield
expression and resumes execution of the generator.If the generator is not at a
yield
expression when this method is called, it will first be let to advance to the firstyield
expression before sending the value. As such it is not necessary to "prime" PHP generators with a Generator::next() call (like it is done in Python).
Emphasis on "As such it is not necessary to "prime" PHP generators with a Generator::next()
". OK, but what does that really mean? I don't have to "prime" it like the JavaScript example, but the first yielded value is also getting swallowed.
Can anyone explain how you're meant to step through generators without using a foreach
?
The first yielded value wasn't swallowed, you just never looked at it.
$g = powGenerator();
echo $g->current(); //a
You're then twice sending in values and resuming execution, $g->valid()
is true
after this because you haven't resumed after the third yield
- the generator isn't complete and there may be more for it to do. Consider:
function powGenerator() {
yield pow((yield 'a'), (yield 'b'));
echo "Okay, finishing here now!\n";
}
$g = powGenerator();
echo $g->current(), "\n"; //a
echo $g->send(10), "\n"; //b
echo $g->send(2), "\n"; //100
$g->next(); // Resumes execution of the generator,
// which prints its own message and completes.
var_dump($g->valid()); //false
This'll output:
a
b
100
Okay, finishing here now!
bool(false)
Now in PHP 7 you can return from a generator.
function powGenerator() {
return pow((yield 'a'), (yield 'b'));
echo "This will never print.";
}
$g = powGenerator();
echo $g->current(), "\n"; //a
echo $g->send(10), "\n"; //b
echo $g->send(2), "\n"; // Prints just the newline, you're moving on
// to a return which you must get explicitly.
var_dump($g->valid()); // Generator complete, you're free to get the return.
echo $g->getReturn(), "\n";
Which outputs:
a
b
bool(false)
100
As for stepping through them without a foreach
- Generator implements Iterator, so it's got appropriate methods to treat it as such: current, key, next, rewind, and valid. With the caveat that rewind
will throw an exception if you call it on a generator that's already begun.
An example which does this and also demonstrates PHP 7's new generator delegation:
function letterGenerator() {
yield from range('a', 'z');
}
$g = letterGenerator();
while ($g->valid()) {
echo $g->current();
$g->next();
}
Output:
abcdefghijklmnopqrstuvwxyz
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With