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