首先讓我們看下PHP官方文件中是怎麼介紹的吧:生成器提供了一種更容易的方法來實現簡單的物件迭代,相比較定義類別實作Iterator 介面的方式,效能開銷和複雜性大大降低。
看了這句話,我們可以獲得幾個關鍵字: 物件迭代、 Iterator 介面、效能開銷 ,比較抽象,talk is cheap show me the code,下面我們從一個生成器最經典的例子開始吧。
PHP中的range() 函數在使用的時候會在記憶體中建立一個包含指定範圍單元的陣列並返回,一般來說,這個並沒什麼不妥,但是當所傳的limit入參值很大的時候,那麼也意味著將會在記憶體中創建的陣列也會很大,這個就太恐怖了,這是要乾爆記憶體的節奏啊。此時我們可以透過生成器來實作一個更有效率的range函數,(下面的程式碼是把PHP官方文件精簡處理了一下):
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並不需要分配大塊內存來存放變量,大大節約了內存,提升效率。
現在我們來總結下生成器和普通函數有哪些異同:
#生成器中必須包含yield關鍵字(用來產生結果),而且可以是多次出現,在普通函數中向外部傳回結果只能使用return,且函數執行完畢;
#一個生成器不可以透過return回傳值,這樣做會產生一個編譯錯誤PHP Fatal error: Generators cannot return values using "return"(注意:這個在PHP7下面不會出錯,但是會終止生成器繼續執行,即調valid()方法會返回false,然而在PHP5中return空是一個有效的語法並且它將終止生成器繼續執行)
Generator 物件是從生成器傳回的,上面程式碼$gen就是一個生成器物件。注意,生成器物件和其他類別的物件不同,它並不能透過new關鍵字創建,只能從生成器函數取得。首先我們看下Generator類別摘要來看看其組成:
Generator implements Iterator { /** * 返回当前产生的值(yield后面表达式的值) */ public mixed current ( void ) /** * yield的键(yield 'key'=>'val';) */ public mixed key ( void ) /** * 从上一个yield之后继续执行,直到下一个yield */ public void next ( void ) /** * 重置迭代器(对于生成器并没什么卵用) */ public void rewind ( void ) /** * 向生成器中传入一个值,并从上一个yield之后继续执行 */ public mixed send ( mixed $value ) /** * 向生成器中抛出一个异常,并从上一个yield之后继续执行 */ public void throw ( Exception $exception ) /** * 检查迭代器是否被关闭(false表示已关闭) */ public bool valid ( void ) /** * 序列化回调,但是生成器不能被序列化,因此会抛出一个异常 */ public void __wakeup ( void ) }
從上面的類別摘要可以看出,Generator類別是實作Iterator介面的,因此它具有迭代器的特性。另外它加入了send()、throw()和__wekeup()方法,相關方法說明已經寫了註釋,在此不再贅述。
寫了一堆,發現我的文筆不好,還是畫個圖感受一下吧(圖畫的也不美觀,大家湊合著看吧,2333)
接下來讓我們看下yield關鍵字,它最簡單的呼叫形式看起來像普通函數的return,不同之處在於普通return會傳回值並終止函數的執行,而yield會傳回一個值給循環呼叫此生成器的程式碼,並且只是暫停執行生成器函數。
這是一個典型的yield表達式:$data = yield $key => $value;,表達式包括兩部分:
注意:PHP5需要加上括號$data = (yiel<br/>d $key => $value);,否則會產生一個編譯錯誤,PHP7就不用關心這個了
其一,是yield後面的表達式,這個可以是單一值也可以是鍵值對,與array中的一個元素對應,這部分錶達式是傳回給上層呼叫的,也就是上層可以透過current方法接收到值或是執行send方法的回傳值;
另外一塊就是yield關鍵字本身,個人把他理解成一個接收器,會收到send方法傳入的值,這個值就是整個yield表達式目前的值,可以被左邊的變數接收。
這麼說可能有點抽象,還是上圖吧:
PHP7新增了yield from關鍵字,該語法開始允許從其他的generator,Traversable對象,或陣列透過yield from 產生數函數來yield值。 yield from的各種特性與yield一樣都是產生數據,只是後面跟隨的表達式不同。下面看個例子(摘自PHP官方文檔):
function count_to_ten() { yield 1; yield 2; yield from [3, 4]; //生成数组 yield from new ArrayIterator([5, 6]); //生成可遍历对象 yield from seven_eight(); //生成生成器对象 yield 9; yield 10; } function seven_eight() { yield 7; yield from eight(); } function eight() { yield 8; } foreach (count_to_ten() as $num) { echo "$num "; } //输出:1 2 3 4 5 6 7 8 9 10
yield from以方便我们编写比较清晰生成器嵌套,这点可以类比于函数中的嵌套调用,当函数A中调用另一个函数B,此时会等B执行完成并返回,方才继续执行。在没有yield from的时候,实现生成器嵌套需要自己实现栈并进行压栈和弹出操作以达到相同效果,那是多么痛苦的操作。
相关推荐:
以上是php生成器詳細介紹的詳細內容。更多資訊請關注PHP中文網其他相關文章!