現代 PHP 新特性系列(四) -- 產生器的建立和使用

來源:互聯網
上載者:User

1、概述

產生器 是PHP 5.5 引入的新特性,但是目測很少人用到它,其實這是個非常有用的功能。

產生器和迭代器有點類似,但是與標準的PHP迭代器不同,PHP產生器不要求類實現Iterator介面,從而減輕了類的開銷和負擔。產生器會根據需求每次計算併產出需要迭代的值,這對應用的效能有很大的影響:試想假如標準的PHP迭代器經常在記憶體中執行迭代操作,這要預先計算出資料集,效能低下;如果要使用特定方式計算大量資料,如操作Excel表資料,對效能影響更甚。此時我們可以使用產生器,即時計算併產出後續值,不佔用寶貴的記憶體空間。

2、建立產生器

產生器的建立方式很簡單,因為產生器就是PHP函數,只不過要在函數中一次或多次使用 yield 關鍵字。與普通的PHP函數不同的是,產生器從不傳回值,只產出值。下面是一個簡單的產生器實現:

function getLaravelAcademy() {    yield 'http://LaravelAcademy.org';    yield 'Laravel學院';    yield 'Laravel Academy';}

很簡單吧!調用此產生器函數時,PHP會返回一個屬於Generator類的對象,這個對象可以使用 foreach 函數迭代,每次迭代,PHP會要求Generator執行個體計算並提供下一個要迭代的值。產生器的優雅體現在每次產出一個值之後,產生器的內部狀態都會停頓;向產生器請求下一個值時,內部狀態又會恢複。產生器內部的狀態會一直在停頓和恢複之間切換,直到抵達函數定義體的末尾或遇到空的 return 語句為止。我們可以使用下面的代碼調用并迭代上面定義的產生器:

foreach(getLaravelAcademy() as $yieldedValue) {    echo $yieldedValue, PHP_EOL;}

上面代碼輸出如下:

http://LaravelAcademy.orgLaravel學院Laravel Academy

3、使用產生器

下面我們實現一個簡單的函數用於產生一個範圍內的數值,以此說明產生器是如何節省記憶體的。首先我們通過迭代器來實現:

function makeRange($length) {    $dataSet = [];    for ($i=0; $i<$length; $i++) {        $dataSet[] = $i;    }    return $dataSet;}$customRange = makeRange(1000000);foreach ($customRange as $i) {    echo $i . PHP_EOL;}

此時執行會報錯,提示超出單個PHP進程記憶體限制(要為100萬個數字提供記憶體空間):

下面我們來改進實現方案,使用產生器實現如下:

function makeRange($length) {    for ($i=0; $i<$length; $i++) {        yield $i;    }}foreach (makeRange(1000000) as $i) {    echo $i . PHP_EOL;}

再次執行就可以毫無壓力的列印出結果,因為產生器每次只需要為一個整數分配記憶體。

此外,一個常用的使用案例就是使用產生器迭代流資源(檔案、音頻等)。假設我們想要迭代一個大小為4GB的CSV檔案,而虛擬私人伺服器(VPS)只允許PHP使用1GB記憶體,因此不能把整個檔案都載入到記憶體中,下面的代碼展示了如何使用產生器完成這種操作:

function getRows($file) {    $handle = fopen($file, 'rb');    if ($handle == FALSE) {        throw new Exception();    }    while (feof($handle) === FALSE) {        yield fgetcsv($handle);    }    fclose($handle);}foreach ($getRows($file) as $row) {    print_r($row);}

上述樣本一次只會為CSV檔案中的一行分配記憶體,而不會把整個4GB的CSV檔案都讀取到記憶體中。

4、總結

產生器是功能多樣性和簡潔性之間的折中方案,產生器只是向前進的迭代器,這意味著不能使用產生器在資料集中執行後退、快進或尋找操作,只能讓產生器計算併產出下一個值。迭代大型資料集或數列時最適合使用產生器,因為這樣佔用的系統記憶體最少。產生器也能完成迭代器能完成的簡單任務,而且使用的代碼更少。

總而言之,產生器並沒有為PHP添加新功能,不過使用產生器大大簡化了某些任務,而且使用的記憶體更少,如果需要更多功能,例如在資料集中執行後退、快進以及尋找功能,最好自己編寫實現Iterator介面的類,或者使用PHP標準庫(SPL)中某個原生的迭代器( http://php.net/manual/spl.iterators.php )。

  • 聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.