Php5.5新特性 Generators詳解
在PHP5.5.0版本中,新增了生成器(Generators)特性,用於簡化實現迭代器介面(Iterator)建立簡單的迭代器的複雜性。
通過生成器,我們可以輕鬆的使用foreach
迭代一系列的資料,而不需要事先在記憶體中構建要被迭代的物件,大大減少了記憶體開銷。
當生成器函式被呼叫的時候,它會返回一個可迭代的物件,當對該物件進行迭代的時候,PHP將會在需要的時候呼叫生成器函式,並且在生成器使用新增的關鍵字yield
產生一個新的值的時候,儲存迭代器內部的狀態。迭代器沒有新的值需要產生的時候,生成器函式就可以直接退出,外部函式繼續執行。
注意,在生成器函式中,不能使用
return
語句返回值,使用return
返回值的話會產生編譯器錯誤。但是,使用空的return
是可以的,它會使迭代器終止。
生成器函式與普通函式一樣的,唯一的區別函式內使用了yield
關鍵字。yield
語句可以說是生成器函式的核心,簡單來說,yield
就像return
語句一樣,區別是return
語句返回後函式就結束了,而使用yield
返回後,只是暫停了函式的執行,轉到外部函式繼續執行,下次呼叫生成器函式的時候,繼續執行生成器函式內部的程式碼。
一個簡單的例子 - 生成器版本的range函式
一個簡單的例子是使用foreach
迭代函式range
的返回值,如果呼叫的是range(0, 1000000)
的話,將會消耗超過100M的記憶體。而使用生成器的話,可能只需要消耗1KB記憶體都不到。
<?php
function xrange($start, $end) {
if ($start > $end) {
throw new RuntimeException("起始值不能大於截止值");
}
for ($i = $start; $i <= $end; $i += 1) {
// 使用yield關鍵字,每次到這裡函式都會返回$i的值,並且控制權交給外部函式繼續執行
yield $i;
}
}
foreach (xrange(1, 9) as $number) {
echo "$number ";
}
上面的例子輸出如下:
上述例子中,我們建立了一個名為xrange
的函式,函式中使用yield
不斷產生返回值,而呼叫xrange(1, 9)
將會建立一個生成器物件。我們可以修改foreach
這一行列印出xrange
物件看看
...
$xrange_res = xrange(1, 9);
var_dump($xrange_res);
foreach( $xrange_res as $number){
...
輸出
可以看出,執行xrange(1, 9)
的時候確實是返回了一個Generator
物件。
使用Generator物件的send方法
在上面的例子中,我們使用yield
語句的時候都是作為單獨的一行語句執行的,也就是yield
語句產生結果給外部,那麼在迭代過程中有沒有辦法從生成器函式外部獲取值呢?
辦法總是有的,因為呼叫生成器函式後返回的是一個Generator
物件,因此我們可以通過呼叫該物件的send
方法從外部給生成器函式傳遞一個值,在呼叫send
方法之後,yield
會收到send
函式傳送的值。
<?php
function gen() {
$ret = (yield 'yield1');
var_dump("-->" . $ret);
$ret = (yield 'yield2');
var_dump("-->" . $ret);
}
$gen = gen();
var_dump($gen->current());
var_dump($gen->send('ret1'));
var_dump($gen->send('ret2'));
輸出:
這裡我們首先建立了名為gen
的生成器物件,然後列印$gen->current()
方法的返回值,該返回值就是迭代器第一次迭代時產生的當前值,因此輸出了yield1
。
接下來我們呼叫了$gen->send('ret')
方法,這時,生成器內第一個yield
語句返回該方法傳遞的值ret1
,因此輸出了$ret
的值為ret1
。
接著由於生成器內部執行到了第三條語句$ret = (yield 'yield2')
,因此外部的第二個var_dump
輸出了yield2
。最後呼叫$gen->send('ret2')
與第一次類似,不過這次生成器內部呼叫yield
之後已經沒有yield
了,因此返回的是NULL
。
注意,這裡的
$ret = (yield 'yield2')
語句中,使用括號包含了yield 'yield2'
語句,這裡是必須的,如果在表示式上下文中使用yield
,必須將yield
放在括號內,否則會報錯。
返回關聯陣列
前面的例子中,我們使用yield
關鍵字返回的總是單個值,實際上PHP也對返回關聯陣列提供了支援,基本語法:
yield key => val
使用該語法格式可以在foreach的時候,返回與遍歷管理陣列相同的結果。
<?php
function gen2() {
$array = [
'username' => 'mylxsw',
'site' => 'http://aicode.cc'
];
foreach ($array as $key => $val) {
yield $key => $val;
}
}
foreach(gen2() as $key => $val) {
var_dump($key . ' : ' . $val);
}
輸出:
使用引用
我們還可以讓生成器以引用的方式返回資料,這樣就可以在生成器外部直接修改生成器內部資料的值。
<?php
function &gen_reference() {
$value = 3;
while ($value > 0) {
yield $value;
}
}
foreach (gen_reference() as &$number) {
echo (--$number).'... ';
}
上述例子中,需要注意的是,生成器函式的定義和遍歷的時候使用了&$number
。
最後,生成器與自定義的迭代器物件是不完全相同的,生成器一旦開始迭代,就不能再rewind
了,只能一直向前迭代,直到迭代完成。如果希望多次迭代一個生成器物件的話,可以多次呼叫生成器函式建立新的生成器物件或者是使用clone關鍵字。
參考:
相關文章
- PHP5.5 ~ PHP7.2 新特性整理PHP
- MySQL 5.5新特性詳解MySql
- MySQL 5.7 新特性詳解MySql
- JDK8新特性詳解JDK
- JDK9新特性詳解JDK
- JDK10新特性詳解JDK
- JDK11新特性詳解JDK
- JDK12新特性詳解JDK
- JDK13新特性詳解JDK
- Java8 新特性詳解Java
- 詳解C#7.0新特性C#
- Android Studio 新特性詳解Android
- JDK8新特性詳解(一)JDK
- JDK8新特性詳解(二)JDK
- ES6新特性Promise詳解Promise
- 收藏版:《JDK13新特性詳解》JDK
- Oracle 18c新特性詳解 - 表和表空間相關的新特性Oracle
- java JDK1.7版本新特性詳解JavaJDK
- 最權威的 Android Oreo 新特性詳解Android
- Oracle Database 12.2新特性詳解 --該國強OracleDatabase
- Python高階特性(1):Iterators、Generators和itertoolsPython
- Dubbo3詳解(5大新特性及功能圖解)圖解
- Oracle 18c新特性詳解:In-Memory 專題Oracle
- 學習Source Generators之瞭解Source Generators的應用場景
- Swift 5新特性詳解:ABI 穩定終於來了!Swift
- Oracle 18c新特性詳解-多租戶專題Oracle
- 全面煥新|詳解 Grafana v9.0.x 新增功能特性Grafana
- Win10 Build 17692釋出 7個新特性詳解Win10UI
- C#–特性詳解C#
- 整理了一份php8新特性詳解pdf文件PHP
- 顯微鏡下的webpack4的新特性:mode詳解Web
- TypeScript GeneratorsTypeScript
- 用Generators解決callback金字塔
- ES10特性詳解
- 蒲公英 · JELLY技術週刊 Vol.14: Vue 3 新特性詳解Vue
- C# 9.0新特性詳解系列之三:模組初始化器C#
- React 16 新特性全解(上)React
- Apache Kyuubi 1.6.0 新特性解讀Apache