Manual Generator (Generator)
- solvable problem
Quoted from the official website: Generators provide an easier way to implement simple object iteration, with greatly reduced performance overhead and complexity compared to defining classes that implement the Iterator interface. Generators allow you to write code in a foreach block to iterate over a set of data without creating an array in memory, which would push your memory limit (emphasis added), or take up considerable processing time. Instead, you can write a generator function, just like a normal custom function, and instead of returning only once, the generator can yield as many times as needed in order to generate the values that need to be iterated over.
- Generate implements the Iterator interface
<?php //Builder Generator implements Iterator { //return the current value public mixed current ( void ) //return the currently generated key public mixed key ( void ) //Generator continues execution public void next ( void ) //Reset the iterator, if the iteration has already started, an exception will be thrown here. public void rewind ( void ) //Pass a value to the generator, the current yield receives the value, and then proceeds to the next yield public mixed send ( mixed $value ) // throw an exception into the generator public void throw ( Exception $exception ) //Check if the iterator is closed, return FALSE if it has been closed, otherwise return TRUE public bool valid ( void ) // serialize callback public void __wakeup ( void ) //Return the return value of the generator function, PHP version 7+ public mixed getReturn ( void ) } ?>
- keyword yield
yield can only be used in functions, otherwise it will report PHP Fatal error: The "yield" expression can only be used inside a function, any function that uses the yield keyword will return a Generator object. Each time the code executes to the yield statement, the execution will be terminated, and the value of the expression in the yield statement will be returned to the Generator object. When the Generator object continues to be iterated, the code following the yield will continue to be executed until all yield statements are executed or there is a return statement. This The renturn statement can only return null, that is, return;, otherwise a compilation error will occur.
- Understand the execution process and usable functions from examples
1 Example one
<?php function xrang($start, $end, $step=1){ for($i=$start; $i<=$end; $i += $step) { yield $i; //yield keyword defines the breakpoint } } //foreach (xrang(1, 10000) as $num) { // echo $num."\n"; //} $rang = xrang(1,2); var_dump($rang).PHP_EOL; //Output: object(Generator)#1 (0) {} var_dump($rang instanceof Iterator).PHP_EOL; //输出: bool(true) $ key = $ rang-> key (); var_dump("key: ".$key).PHP_EOL; //输出: string(6) "key: 0" $valid = $rang->valid(); var_dump("valid: ".$valid).PHP_EOL; //输出: string(8) "valid: 1" $current = $rang->current(); var_dump("current: ".$current).PHP_EOL; //输出: string(10) "current: 1" $rang->next(); $ key = $ rang-> key (); var_dump("key: ".$key).PHP_EOL; //输出: string(6) "key: 1" $valid = $rang->valid(); var_dump("valid: ".$valid).PHP_EOL; //输出: string(8) "valid: 1" $current = $rang->current(); var_dump("current: ".$current).PHP_EOL; //输出: string(10) "current: 2" $rang->next(); $ key = $ rang-> key (); var_dump("key: ".$key).PHP_EOL; //输出: string(5) "key: " $valid = $rang->valid(); var_dump("valid: ".$valid).PHP_EOL; //输出: string(7) "valid: " //$rang->rewind(); //Reset, in all the documents I have seen so far, rewind() is only implicitly executed when the Generator is called for the first time. Calling after the generator has started to iterate will throw a Fatal error. ?>2 Example two
<?php function gen(){ echo "1111\n"; $ ret = (yield 'yield1'); var_dump ($ ret); echo "2222\n"; $ ret = (yield 'yield2'); var_dump ($ ret); //return; } $gen = gen(); var_dump($gen->current()).PHP_EOL; $a = $gen->send('ret1'); echo "66666\n"; var_dump($a).PHP_EOL; echo "77777\n"; var_dump($gen->valid()).PHP_EOL; $b = $gen->send('ret2'); var_dump($b).PHP_EOL; var_dump($gen->valid()).PHP_EOL; //1111 //string(6) "yield1" //string(4) "ret1" //2222 //66666 //string(6) "yield2" //77777 //bool(true) //string(4) "ret2" //NULL //bool(false) ?>2.1 The execution process is:
1. First call gen(), enter the function output 1111, execute to the position where the first yield keyword is located, and interrupt (the value of the yield expression is the defined "yield1" at this time, use current() to get it The value of the current expression is string(6) "yield1")
2. Call the send() method to pass in the value "ret1" to the generator (the value passed into the generator. This value will be used as the generator's current location The return value of the yield), at this time, the generator starts to iterate from the current yield expression, and the program continues to execute
. 3. When encountering var_dump, output the value of the current expression "ret1", and continue to output 2222.
4. Continue to execute, The program comes to the second yield breakpoint. At this time, the value of the expression is the defined value "yield2", because the send() method is called, which returns the current value of the yield (the current() method value). (Check the official documentation of the send method)
5. $a gets the return value of the send method, which is "yield2", and continues to execute to output "66666", $a, "77777"
6. Output whether the current generator is available
7. Continue to execute, Pass the value "ret2" into the generator, and the generator will continue to iterate. The generator is now in the second yield expression, which accepts "ret2" as the return value and assigns it to the variable $ret, and prints out string(4) "ret2".
8. After printing, $b == NULL,
8.1 One may be because there is no breakpoint after the generator and no return value (return value is not allowed, or only return is allowed; return; is used to terminate the execution of the generator), $gen->send() method Nothing is returned at all, resulting in $b == NULL
8.2 Both may be $gen->send('ret2') after passing in the value, the generator iterates this yield, implicitly calling next() and current(), and because there is no yield breakpoint under next(), current() returns NULL, resulting in the return value of send() NULL 8.3 According to the context, the second possibility is more likely
2.2 About the send() method
send() generates A value is passed into the generator and used as the result of the yield expression, and the generator continues to execute. If the generator is not in a yield expression when this method is called, it will run to the first yield expression before passing in a value.