初次接觸迭代器與生成器是在Python中,之後瞭解到在 php5.5 中也引入了生成器的特性,
但很多PHP開發者或許都不知道生成器這個功能,可能是因為平時使用場景較少吧。
但是,生成器功能的確非常有用。
優點:
生成器會對PHP應用的效能有非常大的影響
PHP程式碼執行時節省大量的記憶體
比較適合計算大量的資料
使用一個簡單的例子說明(迭代輸出從1開始到10000的陣列,步進為1):
<?php$start_mem = memory_get_usage();$arr = range( 1, 10000 );foreach( $arr as $value ){ //echo $value.',';}$end_mem = memory_get_usage();echo " use mem : ". ( $end_mem - $start_mem ) .'bytes'.PHP_EOL;
輸入結果:
<?php$start_mem = memory_get_usage();function xrange($start, $limit, $step = 1) { for ($i = $start; $i <= $limit; $i += $step) { // 注意變數$i的值在不同的yield之間是保持傳遞的。 yield $i; }}foreach( xrange( 0, 10000 ) as $value ){ echo $value.PHP_EOL;}$end_mem = memory_get_usage();echo " use mem : ". ( $end_mem - $start_mem ) .'bytes'.PHP_EOL;
輸入結果:
首先從執行結果上來看,528440bytes
與32bytes
這兩個記憶體消耗就一目瞭然了,
在生成器中提供了一種更容易的方法來實現簡單的物件迭代(迴圈),
相比較定義類實現 Iterator
介面的方式,效能開銷和複雜性大大降低。
相關程式碼剖析
這裡使用示例程式碼,在命令列下執行,加上 sleep(1)
可使執行結果更加明顯
<?phpfunction xrange($start, $limit, $step = 1) { // echo '生成器開始執行' . PHP_EOL; for ($i = $start; $i <= $limit; $i += $step) { // echo '產生資料之前:' . $i . PHP_EOL; yield $i; // echo '產生資料之後:' . $i . PHP_EOL; } // echo '再來一個資料' . PHP_EOL; yield 100; // echo '生成器執行結束' . PHP_EOL;}$arr = xrange( 0, 5 );// echo '生成器開始執行了嗎?' . PHP_EOL;sleep(1);foreach( $arr as $value ){ sleep(1); // echo '使用資料前' . PHP_EOL; echo '使用資料:' . $value . PHP_EOL; // echo '使用資料後' . PHP_EOL;}
輸出結果:
使用資料:0使用資料:1使用資料:2使用資料:3使用資料:4使用資料:5使用資料:100
我們可以看到資料在一行一行的輸出,接著我們去掉程式碼中的註釋,再次執行一遍
輸出結果:
生成器開始執行了嗎?生成器開始執行產生資料之前:0使用資料前使用資料:0使用資料後產生資料之後:0產生資料之前:1使用資料前使用資料:1使用資料後產生資料之後:1...產生資料之前:5使用資料前使用資料:5使用資料後產生資料之後:5再來一個資料使用資料前使用資料:100使用資料後生成器執行結束
還原一下程式碼執行過程:
首先呼叫
xrange
函式(生成器),傳入( 0, 5 )
,這裡我們看到生成器並沒有開始執行foreach
開始對$arr
迴圈,執行生成器,接著for
產生第一個資料,將資料0
返回到foreach
中,第一次for
迴圈結束foreach
準備第二次迴圈,接著for
產生第二個資料,將資料1
返回到foreach
中,第二次foreach
迴圈結束,第二次for
迴圈結束這裡
foreach
迴圈迴圈6
次,for
迴圈六次,至此for
迴圈結束foreach
迴圈第七次,輸出生成器中最後一個數100
,到此foreach
迴圈結束
從程式碼中我們看到,始終只有一個記錄值參與迴圈,記憶體中也只有一條資訊。
無論開始傳入的 $arr
有多大,由於並不會立即生成所有結果集,所以記憶體始終是一條迴圈的值
生成器函式的核心 – yield關鍵字
yield
最簡單的呼叫形式看起來像一個 return
申明,不同之處在於普通 return
會返回值並終止函式的執行,而 yield
會返回一個值給迴圈呼叫此生成器的程式碼並且只是暫停執行生成器函式。。
概念理解
到這裡,你應該已經大概理解什麼是生成器了。下面我們來說下生成器原理。
首先明確一個概念:生成器 yield
關鍵字不是返回值,他的專業術語叫產出值,只是生成一個值
那麼程式碼中 foreach
迴圈的是什麼?其實是PHP在使用生成器的時候,會返回一個 Generator
類的物件。foreach
可以對該物件進行迭代,每一次迭代,PHP會通過 Generator
例項計算出下一次需要迭代的值。這樣 foreach
就知道下一次需要迭代的值了。
而且,在執行中 for
迴圈執行後,會立即停止。等待 foreach
下次迴圈時候再次和 for
索要下次的值的時候,for
迴圈才會再執行一次,然後立即再次停止。直到不滿足條件不執行結束。
本作品採用《CC 協議》,轉載必須註明作者和本文連結