今天學到了一個新技術:產生器(Generators)。
一句話來表示的話,產生器就是簡單版的迭代器,往常在設計一個function去做計算的時候,都會去令一個回傳變數,運算完把值賦予給回傳變數之後,再return 回傳變數,這樣一來,如果回傳變數非常大,那麽會佔用非常可觀的記憶體空間。
於是產生器應運而生,不但可以做到完全相同的事,而且每次只會佔用到非常微小的記憶體空間,每一次經歷迭代的時候,php才會讓產生器計算出下一個迭代的值,以下來實作範例:
這邊的dataset會被配置一百萬個記憶體空間,如果使用產生器來實作的話會變成:
一樣的結果,但是在任何時刻只會配置到一組記憶體空間!
每一次的foreach才會讓產生器產出一個數值,產出之後會在當下暫停,在被呼叫下一個數值的時候恢復運作,一直到函式結束或沒有回傳值的return才會結束。
另外,產生器還有另一個驚喜的作用:迭代串流來源!
假設我們必須在一個只有1GB 可用記憶體的VPS裡讀取一個4GB的CSV檔,沒有辦法將整個檔案全部裝進記憶體中,這個時候就可以使用產生器的特性了:
每一次的foreach呼叫產生器取值,而每一次的取值只會用到print_r所呼叫的記憶體,而不是先將整整4GB的讀進記憶體裡,再印出來。
Ps. PHP_EOL的用法類似\n,都是換行。
參考資料:
https://www.tenlong.com.tw/items/9863477788?item_id=1007177
http://blog.ircmaxell.com/2012/07/what-generators-can-do-for-you.html
一句話來表示的話,產生器就是簡單版的迭代器,往常在設計一個function去做計算的時候,都會去令一個回傳變數,運算完把值賦予給回傳變數之後,再return 回傳變數,這樣一來,如果回傳變數非常大,那麽會佔用非常可觀的記憶體空間。
於是產生器應運而生,不但可以做到完全相同的事,而且每次只會佔用到非常微小的記憶體空間,每一次經歷迭代的時候,php才會讓產生器計算出下一個迭代的值,以下來實作範例:
function makeRange($length) { $dataset = array(); for ($i = 0; $i < $length; $i++) { $dataset[] = $i; } return $dataset; } $customRange = makeRange(1000000); foreach ($customRange as $i) { echo $i, PHP_EOL; }
這邊的dataset會被配置一百萬個記憶體空間,如果使用產生器來實作的話會變成:
function makeRange($length) { for ($i = 0; $i < $length; $i++) { yield $i; } } $customRange = makeRange(1000000); foreach ($customRange as $i) { echo $i, PHP_EOL; }
一樣的結果,但是在任何時刻只會配置到一組記憶體空間!
每一次的foreach才會讓產生器產出一個數值,產出之後會在當下暫停,在被呼叫下一個數值的時候恢復運作,一直到函式結束或沒有回傳值的return才會結束。
另外,產生器還有另一個驚喜的作用:迭代串流來源!
假設我們必須在一個只有1GB 可用記憶體的VPS裡讀取一個4GB的CSV檔,沒有辦法將整個檔案全部裝進記憶體中,這個時候就可以使用產生器的特性了:
function getRows($file) { $handle = fopen($file, 'rb'); if ($handle === false) { throw new Exception(); } while (feof($handle) === false) { yield fgetcsv($handle); } fclose($handle); } foreach (getRows('data.csv') as $row) { print_r($row); }
每一次的foreach呼叫產生器取值,而每一次的取值只會用到print_r所呼叫的記憶體,而不是先將整整4GB的讀進記憶體裡,再印出來。
Ps. PHP_EOL的用法類似\n,都是換行。
參考資料:
https://www.tenlong.com.tw/items/9863477788?item_id=1007177
http://blog.ircmaxell.com/2012/07/what-generators-can-do-for-you.html
沒有留言:
張貼留言