PHP yield 生成器 PHP性能優化利器:生成器 yield理解

 

yield 調用

PHP中的range() 函數在使用的時候會在內存中創建一個包含指定範圍單元的數組並返回,一般來說,這個並沒什麼不妥,但是當所傳的limit入參值很大的時候,那麼也就意味着將會在內存中創建的數組也會很大,內存無法承受。此時我們可以通過生成器來實現一個更高效的range函數

function xrange($start, $limit, $step = 1)
{
    //校驗參數,此處省略
    for ($i = $start; $i <= $limit; $i += $step) 
    {
        //向外產出值
        yield $i;
    }
}

//xrange此時返回的是一個生成器對象
$gen = xrange(1, 9);
//對生成器進行迭代
foreach ($gen as $number) 
{
    echo "$number ";
}

 這裏在xrange和range函數的效果相同,均是產生了一個可迭代的變量,但是不同的是,range函數有點像ORM裏面常說的 預加載 ,而xrange則是懶加載只是等到 迭代到那個點(循環到當前的值再運行一次xrange 這個函數)纔會產生對應的值,因此xrange並不需要分配大塊內存來存放變量,大大節約了內存,提升效率。

 

yield 的特性

  1. yield只能用於函數內部,在非函數內部運用會拋出錯誤。

  2. 如果函數包含了yield關鍵字稱爲"生成器函數",那麼函數執行後的返回值(一個可遍歷的對象)是一個Generator對象。

  3. 如果函數內部同事包含yield和return 該函數的返回值依然是Generator對象,但是在生成Generator對象時,return語句後的代碼被忽略。

  4. Generator類實現了Iterator接口。

  5. 可以通過返回的Generator對象內部的方法,獲取到函數內部yield後面表達式的值。

  6. 可以通過Generator的send方法給yield 關鍵字賦一個值。

  7. 一旦返回的Generator對象被遍歷完成,便不能調用他的rewind方法來重置

  8. Generator對象不能被clone關鍵字克隆

  9. yield表達式:$data =( yield)  , 賦值需要使用括號包裹 yield {$value}

  10.  調用生成器函數的 send() 方法,通過 send 方法可以傳遞參數給 yield 發送一個值作爲 yield 語句的值

  send 方法使用示例:

function  printer () {
    $a = (yield 1);
    yield $a;
    yield 2;
    yield 3;
}
 
$printer  =  printer ();
echo $printer->current();
echo $printer->send("b");
$printer->next();
echo $printer->current();

輸出結果:1  b  2;

send()方法會把值傳遞給上一個yield表達式的結果,然後再執行下一個yield並接受返回值。

首先執行到 $printer->current(); 此時返回的是第一個yield後面的1,所以輸出1,然後停止。然後走到send("b"),這一步會把b賦值給(上一個yield表達式的結果)$a,然後繼續執行一個yield表達式(yield $a;) 並接受返回值,所以再輸出b,然後停止。緊接着執行next();生成器定位到yield 2; 然後執行 current()輸出2。

 

生成器和普通函數有哪些異同

  1. 生成器中必須包含yield關鍵字(用來生成結果),而且可以是多次出現,普通函數中向外部返回結果只能使用return,且函數執行完畢;

  2. 一個生成器不可以通過return返回值,這樣做會產生一個編譯錯誤PHP Fatal error: Generators cannot return values using "return"(注意:這個在PHP7下面不會出錯,但是會終止生成器繼續執行,即調valid()方法會返回false,然而在PHP5中return空是一個有效的語法並且它將會終止生成器繼續執行)

  3. yield關鍵詞,它最簡單的調用形式看起來像普通函數的return,不同之處在於普通return會返回值並終止函數的執行,而yield會返回一個值給循環調用此生成器的代碼,並且只是暫停執行生成器函數。

 

PHP yield 讀取大文件

// 老式讀取方式
function readLocalFile($fileName)
{
  $handle = fopen($fileName, 'r');
  $lins = [];
  while (!feof($handle)) {
      $lines[] = fgets($handle);
  }
  fclose($handle);
  return $lines;
}
// 使用 yield 的特性,來讀取大文件
function readYieldFile($fileName)
{
  $handle = fopen($fileName, 'r');
  while (!feof($handle)) {
      yield fgets($handle);
  }
  fclose($handle);
}
//讀取內存的輔助函數
function formatBytes($bytes)
{
  if ($bytes < 1024) {
      return $bytes . "b";
  } else if ($bytes < 1048576) {
      return round($bytes / 1024, 2) . "kb";
  }
  return round($bytes / 1048576, 2) . 'mb';
}

測試,這裏準備了一個 7M 大小的文本文件來做測試

# 第一種
readLocalFile('./all.txt');
echo formatBytes(memory_get_peak_usage()); // 結果爲 7.59mb

# 第二種
$lines = readYieldFile('./all.txt');
foreach ($lines as $row) {}
echo formatBytes(memory_get_peak_usage()); // 結果爲 137.79kb

 

相關文章:PHP性能優化利器:生成器 yield理解

  php生成器詳細介紹

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章