本篇文章給大家分享的是有關PHP的yield在生成器中的應用,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
關于
yield
特性,是在開發PHP5
時被提上日程,PHP5.5
版本正式加入。
關于yield
的使用,我看到大部分文章都停留在,使用yield
如何在foreach
中穿出數據,今天想給大家講講 生成器 所有語法。
生成器允許你在 foreach
代碼塊中寫代碼來迭代一組數據而不需要在內存中創建一個數組, 那會使你的內存達到上限,或者會占據可觀的處理時間。相反,你可以寫一個生成器函數,就像一個普通的自定義函數一樣, 和普通函數只返回一次不同的是, 生成器可以根據需要 yield
多次,以便生成需要迭代的值。
看了下官網對他講解:php.net 生成器語法 . 每個字都認識,但似乎還是體會到它講的內涵。官網我們主要看兩部分內容:
yield
的語法。
代碼例子。
先說語法, yield 的左邊是一個賦值語句,右邊可以是值(也可是表達式) 。而yield 會先執行右邊的表達式,并把值$value送到生成器外面。當生成器收到值后,會執行yield左邊的語句,賦值給$data.
<?phpfunction func(){ $data = (yield [$express]);}
語法就這樣,估計大家還是有些懵,那就看看官網下面代碼例子吧,我看里面例子參差不齊。
注意yield 外面包的這一層括號,如果是在php5.5,右側$express的優先級是判斷,可能會比左側$data的賦值語句低的。所以在php5用yield,yield 右邊是可運行表達式,左側需要接受返回并賦值,那么這個括號是有必要的。在php7不會有這個問題。
對于一個用人類語言來描述,都不那么明晰時,所以那就通過例子告訴你它能做什么,不能做什么。
相關代碼,我放到gitee了,希望你能復制到你本地運行下,親自運行感受下,有助于了理解接下來的內容。
git clone gitee.com/xupaul/PHP-generator-yie...
先定義一個函數,在函數內 寫個 yield 關鍵詞,將這個函數調用賦值給一個變量。一個生成器就產生了。
代碼 /php-yield-test/yieldFunctions.php 是生成器按照不同語法組合定義了多個生成器。
測試代碼 /php-yield-test/whatIsGenerator.php,用來檢查哪些函數能構成生成器,哪些不能。運行結果如下
yield
關鍵詞,函數可以是全劇函數,或者類的方法。yield
肯定不會被執行,也會產生生成器。見:yield_func4yield
關鍵詞就行(不向外送出,不處理外面的輸入)。見: yield_func2yield
會報錯, 見:yield_func11是的,函數內有沒有foreach,while,for 語句都不是關鍵,關鍵是 yield. 生成器的類型判斷用
$gen instanceof Generator
Generator 對象是從 generators返回的.
Generator 對象不能通過 new 實例化.
摘自 php.net generator
看著以上方法,是不想起了
Iterator
, 他們的確很像。同時注意,官網zh語言版本的文檔沒有索引方法getReturn
,訪問也是404。文檔以en版為準,ch做參考。
以上就是生成器所有的方法,我們一個個來看。
測試方法代碼 /php-yield-test/generatorMothod.php, 這里面對每個方法都有使用舉例,運行結果如下。
好接下來對舉例做個一一講解。
<?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
代碼。
通過第一個代碼事例,可得,對一個generator調用current方法,才算真正開始執行。執行到yield為止。如果不能命中yield,則執行到函數結束。
非generoator會立馬執行并得到結果,而非一個生成器對象。
通過例子2,調用current一次,兩次呢,第一次可以看到代碼執行日志,第二次,只是把上一次的結果返回給我們而已,并不是讓該生成器重新執行。
通過例子1,調用該函數還會獲取到返回值,返回的內容就是 yield 表達式左邊的內容。如果表達式無內容,則是NULL.
yield
點中傳入一個值,并返回下一次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,是一個current,send的常規調用。調用current代碼運行yield等到用戶send輸入參數。接收到輸入后,繼續運行。current能夠接收到yield彈出的值,send返回值為空。
例子4,直接調用send,相當于調用current,send。不過current的返回值,并不會通過send傳給用戶。
例子21中,可以看到直接調用send(1),會運行生成器,并向第一個yield處輸入1,繼續運行至下一個yield的返回值value
。所以,$gen->send(2)
,和 $gen->current()
結果都是同一個值。
也就是說:跳過current,直接調用send,會丟失第一次yield的彈出值。
<?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,這是一個較為常規的調用,調用current
代碼運行yield
等到用戶輸入,這是調用next跳過,讓代碼繼續運行。
例子6,直接調用next
,相當于調用current
,next
。而且通過最后打印$result
, 我們發現怎么有點像在調用 $gen->send(NULL);
。
<?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 中,發現調用該方法,會導致隱式調用current
。
例子9 中,發現在執行過一個yield代碼段后,再次調用該方法,會導致報錯(哪怕該 生成器已結束)。
<?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
通過以上簡單的例子可得,throw 就是讓yield這行代碼產生異常,讓外面的try catch 捕獲我們生成的那個異常。
例子11中,構造生成器,并調用current方法,運行到yield處,再調用throw,就能捕獲到異常。
例子12中,當調用send方法,跳過函數內yield代碼時,再調用throw傳入異常,就沒法捕獲了。
<?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中,發現current被隱式調用。
例子13中,可得,當生成器運行到yield代碼段時,用valid
函數檢查,都會返回true
。
所以,別問我是否已運行,問就是運行。該方法用來獲取是否關閉狀態,不是 是否運行狀態!運行到底,運行到return就是 關閉狀態。
<?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可顯示設置返回的key.
例子15 中,發現key的分發規律和PHP數組鍵值發放策略是差不多的,默認從0開始,未指定則是以上一個數字key+1
作為當前的key
.
例子16 中,我們又發現current
被隱式調用。
<?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
這是一個魔術方法,見 PHP 魔術方法,也就是說 生成器 不能被序列化成一個字符串。
例子17就不用說了,看下例子18,看樣子序列化成功了。也就是說一個生成器做為一個方法可以被序列化,當函數變成生成器時,就不能被序列化了。
<?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
該函數就是獲取生成器最后的返回值。如果沒有return語句,或者沒有執行到return語句,調用該函數得到的就是NULL。
例子19 可得,getReturn 能夠獲取到生成器最后的返回值。
例子19、20 可得,當生成器沒有執行到return語句,或者沒有執行到最后時,調用getReturn是會導致報錯。
到這里,我們就發現rewind
,next
和 __wakeup
這兩個函數感覺沒啥叼用呢,為啥還存在呢,因為Generator
繼承Iterator
,自然就有了rewind
, next
方法,PHP
雖然支持方法覆蓋,但子類的訪問修飾符
不能縮緊,所以Generator
只能重寫這兩個方法。 __wakeup
繼承自 stdClass
。
看圖:
PHP yield 生命周期圖
畫了兩個狀態轉換圖,上面的要細致,繁復一點。下面的精簡版,便于快速理解。
以上就是PHP的yield在生成器中的應用,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。