摘要:所以生成器首先是一個迭代器,也就是說它可以使用進行遍歷。普通函數后,函數會被從棧中移除,中止執行,但是會保存生成器的狀態,當被再次調用時,迭代器會從上次的地方恢復調用狀態繼續執行。
生成器概述
PHP從5.5.0版本開始支持生成器(Generator),根據PHP官方文檔的說法:生成器提供了一種更容易的方法來實現簡單的對象迭代,相比較定義類實現 Iterator 接口的方式,性能開銷和復雜性大大降低。
所以生成器首先是一個迭代器(Iterator),也就是說它可以使用foreach進行遍歷。生成器就類似一個返回數組的函數,它可以接收參數,并被調用。
我們以range()函數為例,把它實現為生成器:
結果看起來是一樣的:
results from range():1 4 7 10 results from xrange():1 4 7 10可以看到,xrange()使用yield關鍵字,而不是return。使用yield關鍵字后,調用函數時就會返回一個生成器(Generator)的對象(Generator是一個內部類,不能直接實例化),這個對象實現了Iterator接口,所以正如前面說過,生成器是迭代器,我們可以通過以下代碼驗證下:
跟普通函數只返回一次值不同的是, 生成器可以根據需要yield多次,以便生成需要迭代的值。 普通函數return后,函數會被從棧中移除,中止執行,但是yield會保存生成器的狀態,當被再次調用時,迭代器會從上次yield的地方恢復調用狀態繼續執行。看下下面代碼的執行結果:
The generator has started return 1 Yielded 1 return 4 Yielded 4 return 7 Yielded 7 return 10 Yielded 10 The generator has ended可以看到,每次迭代,在yield后,代碼不會繼續執行,而是先執行調用者的代碼,然后在下一次迭代,迭代器的代碼繼續執行,一直到沒有yield可以執行為止。
生成器語法 return值前面說過,函數里使用yield關鍵字后,在被調用時會返回一個生成器對象,所以生成器函數的核心是yield關鍵字。它的調用形式看起來像一個return申明,不同之處在于普通return會返回值并終止函數的執行,而yield會返回一個值給循環調用此生成器的代碼并且只是暫停執行生成器函數。
一個生成器函數不可以通過return返回值(很顯而易見,因為生成器函數被調用后返回的是一個生成器對象), 在PHP 5.6版本及之前,如果使用return返回一個值的話,會產生一個編譯錯誤:
PHP Fatal error: Generators cannot return values using "return" in /path/to/php_code.php on line x在PHP 7中,可以使用getReturn()得到return的返回值:
getReturn(); // 1不過有個前提,就是生成器已經完成了迭代,否則會報以下錯誤:
PHP Fatal error: Uncaught Exception: Cannot get return value of a generator that hasn"t returned in /path/to/php_code.php:x另外,return空無論是在PHP 7還是之前支持生成器的PHP版本都是一個有效的語法,它會終止生成器繼續執行。
生成null值如果yield后面沒有跟任何的參數,則會返回NULL值:
輸出:
array(3) { [0]=> NULL [1]=> NULL [2]=> NULL }生成鍵值對PHP的數組支持關聯鍵值對數組,生成器其實也支持生成鍵值對:
$i; } } var_dump(iterator_to_array(gen_key_values()));輸出:
array(3) { ["key0"]=> int(0) ["key1"]=> int(1) ["key2"]=> int(2) }注入值除了生成值,生成器還能從外面接收值。通過生成器對象的send()方法,我們可以從外面傳遞值到生成器里。這個值會作為yield表達式的結果,我們可以利用這個值來做一些計算或者其他事情,例如根據值來中止生成器的執行:
send("stop"); } echo $v . PHP_EOL; }輸出結果:
0 1 2 3send()方法的返回值是下一個yield的值,如果沒有,則返回NULL。
需要注意的是, 如果在一個表達式上下文(例如上面的情況,在一個賦值表達式的右側)中使用yield,必須使用圓括號把yield申明包圍起來。 例如:
$data = (yield $value);下面的代碼在PHP5中會產生一個編譯錯誤:
$data = yield $value;yield from表達式在PHP 7里,使用yield from表達式允許你在生成器里通過其他生成器、Traversable對象或者數組產生值。這種方式叫做生成器委托。下面的例子來自官方文檔:
輸出:
1 2 3 4 5 6 7 8 9 10為什么不使用Iterator生成器也是迭代器,那為什么不直接使用迭代器呢?其實文章剛開始就說到了:生成器提供了一種更容易的方法來實現簡單的對象迭代,相比較定義類實現 Iterator 接口的方式,性能開銷和復雜性大大降低。
更低的復雜度要使用迭代器,必須要實現Iterator接口里的所有方法,這無疑大大增加了使用成本,具體可以看看官方文檔里的例子:Comparing generators with Iterator objects。
更低的內存占用除了復雜度,另外一個使用生成器的原因就是使用生成器可以大大減少內存的使用。以文章最開始的例子為例,標準的 range() 函數需要在內存中生成一個數組包含每一個在它范圍內的值,然后返回該數組,這樣就會產生多個很大的數組。 比如,調用 range(0, 1000000) 將導致內存占用超過 100 MB。而我們實現的xrange()生成器, 只需要足夠的內存來創建 生成器對象并在內部跟蹤生成器的當前狀態,這樣只需要不到1K字節的內存。
測試結果:
Test for range(): time:0.2319 memory (byte):144376424 Test for xrange(): time:0.1382 memory (byte):0可以看到,在內存占用上,xrange()遠遠低于range(),甚至在速度上也占優。在諸如讀取文件之類的場景,使用生成器也可以大大減少內存的占用:
使用生成器實現協程PHP的生成器特性使得在PHP中實現協程成為了可能,下面是一篇使用協程實現多任務調度的文章,雖然是12年的文章,但是仍然很有參考意義:
http://nikic.github.io/2012/1...
參考http://php.net/manual/zh/lang...
https://www.sitepoint.com/gen...
http://nikic.github.io/2012/1...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/23312.html
摘要:編程書籍的整理和收集最近一直在學習深度學習和機器學習的東西,發現深入地去學習就需要不斷的去提高自己算法和高數的能力然后也找了很多的書和文章,隨著不斷的學習,也整理了下自己的學習筆記準備分享出來給大家后續的文章和總結會繼續分享,先分享一部分的 編程書籍的整理和收集 最近一直在學習deep learning深度學習和機器學習的東西,發現深入地去學習就需要不斷的去提高自己算法和高數的能力然后...
閱讀 1612·2019-08-29 13:53
閱讀 3211·2019-08-29 13:50
閱讀 855·2019-08-27 10:51
閱讀 567·2019-08-26 18:36
閱讀 1798·2019-08-26 11:00
閱讀 605·2019-08-26 10:36
閱讀 3218·2019-08-23 17:58
閱讀 2033·2019-08-23 15:17