按照php的文檔說明
一個生成器函數看起來像一個普通的函數,不同的是普通函數返回一個值,而一個生成器可以yield生成許多它所需要的值。
當一個生成器被調用的時候,它返回一個可以被遍歷的對象.當你遍歷這個對象的時候(例如通過一個foreach循環),PHP 將會在每次需要值的時候調用生成器函數,並在產生一個值之後保存生成器的狀態,這樣它就可以在需要產生下一個值的時候恢復調用狀態。
一旦不再需要產生更多的值,生成器函數可以簡單退出,而調用生成器的代碼還可以繼續執行,就像一個數組已經被遍歷完了。
一個生成器不可以返回值: 這樣做會產生一個編譯錯誤。然而return空是一個有效的語法並且它將會終止生成器繼續執行。
/** * 安原來的寫法,我需要一個方法來整理數據。nums()會返回一個數組或者其他可以迭代的數據 * 然後再遍歷這個數組 */ function nums() { $array = array(); for ($i = 0; $i < 10000; ++$i) { $array[]= $i; } return $array; } foreach (nums() as $v){ var_dump($v); }; /** * 但是用了yield之後,我不再需要創建一個變量,來存儲這個數據。 * 而是在內部會為生成的值配對連續的整型索引,就像一個非關聯的數組。 * 這樣會省掉很大的內存開銷 */ function nums2() { for ($i = 0; $i < 10000; ++$i) { yield $i; } } foreach (nums2() as $v){ var_dump($v); };
$handler=function() { $start = microtime(true); yield; $use_time = (microtime(true) - $start) * 1000; echo "the time is ".(int)$use_time."ms\n"; yield; $use_time = (microtime(true) - $start) * 1000; echo "the time2 is ".(int)$use_time."ms\n"; }; //根據文檔的說明 當一個生成器被調用的時候,它返回一個可以被遍歷的對象.當你遍歷這個對象的時候 //PHP 將會在每次需要值的時候調用生成器函數, //也就是說此時並沒有真的調用$handler中定義的函數。而是返回了一個生成器 $generator = call_user_func_array($handler,array()); if ($generator && $generator instanceof \Generator) { //當對生成器進行迭代的時候,才會真正的調用該$handler中定義的函數 if ($generator->current() === false) { /***do some thing**/ } }
生成器也可以通過引用來使用
/** * 使用引用來生成值 */ function &gen_reference() { $value = 3; while ($value > 0) { yield $value; } } /* * 我們可以在循環中修改$number的值,而生成器是使用的引用值來生成,所以gen_reference()內部的$value值也會跟著變化。 */ foreach (gen_reference() as &$number) { echo (--$number).'... '; }
除了通過引用來改變生成器中的數據之外,我們還可以使用send方法傳遞數據
function printer() { while (true) { $string = yield; echo $string; } } $printer = printer(); $printer->send('Hello world!');
但是上面的例子如果沒有 while(true),那麼無論後面send多少次,該生成器只會執行一次。如此說來,似乎可以用這個來控制本函數調用的最多次數?
除了向裡面傳遞數據之外,還可以throw異常。
function application() { while (true){ try { yield; }catch (Exception $e){ echo $e; } } } $printer = application(); $printer->throw(new Exception("test"));
生成器的嵌套
/** * 在php7中 可以通過yield from 進行嵌套 */ function count_to_ten() { yield 1; yield 2; yield from [3, 4]; yield from new ArrayIterator([5, 6]); yield from seven_eight(); yield 9; yield 10; } function seven_eight() { yield 7; yield from eight(); } function eight() { yield 8; } $gen = count_to_ten(); foreach ($gen as $num) { echo "$num "; } echo "the return value is ".$gen->getReturn();