溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

PHP的yield在生成器中的應(yīng)用

發(fā)布時(shí)間:2020-07-03 09:54:38 來源:億速云 閱讀:197 作者:Leah 欄目:編程語言

本篇文章給大家分享的是有關(guān)PHP的yield在生成器中的應(yīng)用,小編覺得挺實(shí)用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。

關(guān)于yield 特性,是在開發(fā) PHP5 時(shí)被提上日程,PHP5.5 版本正式加入。

關(guān)于yield的使用,我看到大部分文章都停留在,使用yield如何在foreach中穿出數(shù)據(jù),今天想給大家講講 生成器 所有語法。

官網(wǎng)講解

生成器允許你在 foreach 代碼塊中寫代碼來迭代一組數(shù)據(jù)而不需要在內(nèi)存中創(chuàng)建一個(gè)數(shù)組, 那會(huì)使你的內(nèi)存達(dá)到上限,或者會(huì)占據(jù)可觀的處理時(shí)間。相反,你可以寫一個(gè)生成器函數(shù),就像一個(gè)普通的自定義函數(shù)一樣, 和普通函數(shù)只返回一次不同的是, 生成器可以根據(jù)需要 yield 多次,以便生成需要迭代的值。

看了下官網(wǎng)對(duì)他講解:php.net 生成器語法  . 每個(gè)字都認(rèn)識(shí),但似乎還是體會(huì)到它講的內(nèi)涵。官網(wǎng)我們主要看兩部分內(nèi)容:

  1. yield 的語法。

  2. 代碼例子。

先說語法, yield 的左邊是一個(gè)賦值語句,右邊可以是值(也可是表達(dá)式) 。而yield 會(huì)先執(zhí)行右邊的表達(dá)式,并把值$value送到生成器外面。當(dāng)生成器收到值后,會(huì)執(zhí)行yield左邊的語句,賦值給$data.

<?phpfunction func(){
    $data = (yield [$express]);}

語法就這樣,估計(jì)大家還是有些懵,那就看看官網(wǎng)下面代碼例子吧,我看里面例子參差不齊。

注意yield 外面包的這一層括號(hào),如果是在php5.5,右側(cè)$express的優(yōu)先級(jí)是判斷,可能會(huì)比左側(cè)$data的賦值語句低的。所以在php5用yield,yield 右邊是可運(yùn)行表達(dá)式,左側(cè)需要接受返回并賦值,那么這個(gè)括號(hào)是有必要的。在php7不會(huì)有這個(gè)問題。

通過例子來了解它

對(duì)于一個(gè)用人類語言來描述,都不那么明晰時(shí),所以那就通過例子告訴你它能做什么,不能做什么。

相關(guān)代碼,我放到gitee了,希望你能復(fù)制到你本地運(yùn)行下,親自運(yùn)行感受下,有助于了理解接下來的內(nèi)容。

git clone gitee.com/xupaul/PHP-generator-yie...

怎樣才能產(chǎn)生 Generator

先定義一個(gè)函數(shù),在函數(shù)內(nèi) 寫個(gè) yield 關(guān)鍵詞,將這個(gè)函數(shù)調(diào)用賦值給一個(gè)變量。一個(gè)生成器就產(chǎn)生了。

代碼 /php-yield-test/yieldFunctions.php 是生成器按照不同語法組合定義了多個(gè)生成器。

測(cè)試代碼 /php-yield-test/whatIsGenerator.php,用來檢查哪些函數(shù)能構(gòu)成生成器,哪些不能。運(yùn)行結(jié)果如下

PHP的yield在生成器中的應(yīng)用

  1. 函數(shù)內(nèi)必須有 yield 關(guān)鍵詞,函數(shù)可以是全劇函數(shù),或者類的方法。
  2. 哪怕 yield 肯定不會(huì)被執(zhí)行,也會(huì)產(chǎn)生生成器。見:yield_func4
  3. 光禿禿 的 yield 關(guān)鍵詞就行(不向外送出,不處理外面的輸入)。見: yield_func2
  4. 函數(shù)內(nèi)使用 生成器 并不能讓自己也成為生成器,見:yield_func5
  5. eval函數(shù)中直接運(yùn)行 yield 會(huì)報(bào)錯(cuò), 見:yield_func11

是的,函數(shù)內(nèi)有沒有foreach,while,for 語句都不是關(guān)鍵,關(guān)鍵是 yield. 生成器的類型判斷用 $gen instanceof Generator

生成器的函數(shù)

Generator 對(duì)象是從 generators返回的.

Generator 對(duì)象不能通過 new 實(shí)例化.

  • Generator::current — 返回當(dāng)前產(chǎn)生的值
  • Generator::key — 返回當(dāng)前產(chǎn)生的鍵
  • Generator::next — 生成器繼續(xù)執(zhí)行
  • Generator::rewind — 重置迭代器
  • Generator::send — 向生成器中傳入一個(gè)值
  • Generator::throw — 向生成器中拋入一個(gè)異常
  • Generator::valid — 檢查迭代器是否被關(guān)閉
  • Generator::__wakeup — 序列化回調(diào)
  • Gengerator::getReturn - Get the return value of a generator

摘自 php.net generator

看著以上方法,是不想起了Iterator, 他們的確很像。同時(shí)注意,官網(wǎng)zh語言版本的文檔沒有索引方法getReturn,訪問也是404。文檔以en版為準(zhǔn),ch做參考。

以上就是生成器所有的方法,我們一個(gè)個(gè)來看。

測(cè)試方法代碼 /php-yield-test/generatorMothod.php, 這里面對(duì)每個(gè)方法都有使用舉例,運(yùn)行結(jié)果如下。

PHP的yield在生成器中的應(yīng)用

PHP的yield在生成器中的應(yīng)用

好接下來對(duì)舉例做個(gè)一一講解。

Generator::current

  • 返回當(dāng)前產(chǎn)生的值
<?phpfunction yield_func(){
    yield 12;
    return 'a';}$gen = yield_func();$re = $gen->current();echo 'current return : ' . $re;

輸出:

current return : 12

看到 php-yield-test/generatorMothod.php  代碼。

通過第一個(gè)代碼事例,可得,對(duì)一個(gè)generator調(diào)用current方法,才算真正開始執(zhí)行。執(zhí)行到y(tǒng)ield為止。如果不能命中yield,則執(zhí)行到函數(shù)結(jié)束。

非generoator會(huì)立馬執(zhí)行并得到結(jié)果,而非一個(gè)生成器對(duì)象。

通過例子2,調(diào)用current一次,兩次呢,第一次可以看到代碼執(zhí)行日志,第二次,只是把上一次的結(jié)果返回給我們而已,并不是讓該生成器重新執(zhí)行。

通過例子1,調(diào)用該函數(shù)還會(huì)獲取到返回值,返回的內(nèi)容就是 yield 表達(dá)式左邊的內(nèi)容。如果表達(dá)式無內(nèi)容,則是NULL.

Generator::send

  • 向生成器yield點(diǎn)中傳入一個(gè)值,并返回下一次current值。
<?phpfunction yield_func(){
    $data = yield 12;
    echo 'get yield data: ' . $data;
    return 'a';}$gen = yield_func();$re = $gen->current();$gen->send(32);

輸出:

get yield data: 32

例子3,是一個(gè)current,send的常規(guī)調(diào)用。調(diào)用current代碼運(yùn)行yield等到用戶send輸入?yún)?shù)。接收到輸入后,繼續(xù)運(yùn)行。current能夠接收到y(tǒng)ield彈出的值,send返回值為空。

例子4,直接調(diào)用send,相當(dāng)于調(diào)用current,send。不過current的返回值,并不會(huì)通過send傳給用戶。

例子21中,可以看到直接調(diào)用send(1),會(huì)運(yùn)行生成器,并向第一個(gè)yield處輸入1,繼續(xù)運(yùn)行至下一個(gè)yield的返回值value。所以,$gen->send(2),和 $gen->current() 結(jié)果都是同一個(gè)值。

也就是說:跳過current,直接調(diào)用send,會(huì)丟失第一次yield的彈出值。

Generator::next

  • 跳過中斷,并讓生成器繼續(xù)執(zhí)行
<?phpfunction yield_func(){
    echo 'run to code line: ' . __LINE__ . PHP_EOL;
    yield;
    echo 'run to code line: ' . __LINE__ . PHP_EOL;
    return $result;}$gen = yield_func();$gen->current();echo 'current called' . PHP_EOL;$gen->next();

輸出:

run to code line: 4current called
run to code line: 6

例子5,這是一個(gè)較為常規(guī)的調(diào)用,調(diào)用current代碼運(yùn)行yield等到用戶輸入,這是調(diào)用next跳過,讓代碼繼續(xù)運(yùn)行。

例子6,直接調(diào)用next,相當(dāng)于調(diào)用currentnext。而且通過最后打印$result, 我們發(fā)現(xiàn)怎么有點(diǎn)像在調(diào)用 $gen->send(NULL);

Generator::rewind

  • 重置迭代器
<?phpfunction yield_func(){
    echo 'run to code line: ' . __LINE__ . PHP_EOL;
    $result = yield 12;
    echo 'run to code line: ' . __LINE__ . PHP_EOL;}$gen = yield_func();echo 'call yield_func rewind ' . PHP_EOL;$gen->rewind();

輸出:

call yield_func rewind 
run to code line: 4

例子7,8 中,發(fā)現(xiàn)調(diào)用該方法,會(huì)導(dǎo)致隱式調(diào)用current。

例子9 中,發(fā)現(xiàn)在執(zhí)行過一個(gè)yield代碼段后,再次調(diào)用該方法,會(huì)導(dǎo)致報(bào)錯(cuò)(哪怕該 生成器已結(jié)束)。

Generator::throw

  • 向生成器中拋入一個(gè)異常
<?phpfunction yield_func(){
    try {
        $re = yield 'exception';
    } catch (Exception $e) {
        echo 'catched exception msg: ' .$e->getMessage();
    }}$gen = yield_func();$gen->throw(new \Exception('new yield  exception'));

輸出:

catched exception msg: new yield  exception

通過以上簡(jiǎn)單的例子可得,throw 就是讓yield這行代碼產(chǎn)生異常,讓外面的try catch 捕獲我們生成的那個(gè)異常。

例子11中,構(gòu)造生成器,并調(diào)用current方法,運(yùn)行到y(tǒng)ield處,再調(diào)用throw,就能捕獲到異常。

例子12中,當(dāng)調(diào)用send方法,跳過函數(shù)內(nèi)yield代碼時(shí),再調(diào)用throw傳入異常,就沒法捕獲了。

Generator::valid

  • 檢查迭代器是否被關(guān)閉
<?phpfunction yield_func(){
    yield 12;
    return 'a';}$gen = yield_func();$gen->send(1);$check = $gen->valid();echo 'the generator valid ? ' . intval($check);

輸出:

the generator valid ? 0

例子12中,發(fā)現(xiàn)current被隱式調(diào)用。

例子13中,可得,當(dāng)生成器運(yùn)行到y(tǒng)ield代碼段時(shí),用valid函數(shù)檢查,都會(huì)返回true。

所以,別問我是否已運(yùn)行,問就是運(yùn)行。該方法用來獲取是否關(guān)閉狀態(tài),不是 是否運(yùn)行狀態(tài)!運(yùn)行到底,運(yùn)行到return就是 關(guān)閉狀態(tài)。

Generator::key

  • 返回當(dāng)前產(chǎn)生的鍵
<?phpfunction yield_func(){
    yield 1 => 'abc';}$gen = yield_func();echo 'value is :' . $gen->current() . PHP_EOL;echo 'key is: ' . $gen->key() . PHP_EOL;

輸出:

value is :abc
key is: 1

從以上例子中,可得yield可顯示設(shè)置返回的key.

例子15 中,發(fā)現(xiàn)key的分發(fā)規(guī)律和PHP數(shù)組鍵值發(fā)放策略是差不多的,默認(rèn)從0開始,未指定則是以上一個(gè)數(shù)字key+1作為當(dāng)前的key.

例子16 中,我們又發(fā)現(xiàn)current被隱式調(diào)用。

Generator::__wakeup

  • Generator::__wakeup — 序列化回調(diào)
<?phpfunction yield_func(){
    yield 1 => 'abc';}$gen = yield_func();try {$ser = serialize($gen);} catch (\Exception $e) {
    print_r($e->getMessage());}

輸出:

Serialization of 'Generator' is not allowed

這是一個(gè)魔術(shù)方法,見 PHP 魔術(shù)方法,也就是說 生成器 不能被序列化成一個(gè)字符串。

例子17就不用說了,看下例子18,看樣子序列化成功了。也就是說一個(gè)生成器做為一個(gè)方法可以被序列化,當(dāng)函數(shù)變成生成器時(shí),就不能被序列化了。

Generator::getReturn

<?phpfunction yield_func(){
    yield 1 => 'abc';
    return 32;}$gen = yield_func();$gen->send(0);echo 'call yield_func return, and get: ' . $gen->getReturn();

輸出:

call yield_func return, and get: 32

該函數(shù)就是獲取生成器最后的返回值。如果沒有return語句,或者沒有執(zhí)行到return語句,調(diào)用該函數(shù)得到的就是NULL。

例子19 可得,getReturn 能夠獲取到生成器最后的返回值。

例子19、20 可得,當(dāng)生成器沒有執(zhí)行到return語句,或者沒有執(zhí)行到最后時(shí),調(diào)用getReturn是會(huì)導(dǎo)致報(bào)錯(cuò)。

綜上所述

到這里,我們就發(fā)現(xiàn)rewind,next__wakeup 這兩個(gè)函數(shù)感覺沒啥叼用呢,為啥還存在呢,因?yàn)?code>Generator繼承Iterator,自然就有了rewind, next方法,PHP 雖然支持方法覆蓋,但子類的訪問修飾符 不能縮緊,所以Generator只能重寫這兩個(gè)方法。 __wakeup 繼承自 stdClass

狀態(tài)轉(zhuǎn)換

看圖:
PHP的yield在生成器中的應(yīng)用
PHP yield 生命周期圖

畫了兩個(gè)狀態(tài)轉(zhuǎn)換圖,上面的要細(xì)致,繁復(fù)一點(diǎn)。下面的精簡(jiǎn)版,便于快速理解。

以上就是PHP的yield在生成器中的應(yīng)用,小編相信有部分知識(shí)點(diǎn)可能是我們?nèi)粘9ぷ鲿?huì)見到或用到的。希望你能通過這篇文章學(xué)到更多知識(shí)。更多詳情敬請(qǐng)關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI