理解php中的yield


<?php
function gen() {
    $ret = (yield 'yield1');
    var_dump($ret);
    $ret = (yield 'yield2');
    var_dump($ret);
}
 
$gen = gen();
var_dump($gen->current());    // string(6) "yield1"
var_dump($gen->send('ret1')); // string(4) "ret1"   (the first var_dump in gen)
                              // string(6) "yield2" (the var_dump of the ->send() return value)
var_dump($gen->send('ret2')); // string(4) "ret2"   (again from within gen)
                              // NULL               (the return value of ->send())
?>

 


上面的代碼首先是調用函數gen生成一個Generator對象,然后調用這個對象的current方法返回第一個值,顯然它是第一個yield語句的返回值,也就是'yield1',這個時候gen函數的執行就會被中止,接着執行var_dump($g->send('ret1'));。

調用$g->send('ret1'),傳入參數為字符串'ret1',按照上面的說明,它會賦值給第一個yield表達式,也就是(yield 'yield1')中的yield(注意:這個時候不包括'yield1'),它的值為'ret1',然后會賦值給$ret,所以第二個輸出'ret1'就是gen函數中的第一個var_dump輸出的。此時對Generator對象的迭代會恢復繼續執行,實際上就是調用了一次next函數,它會執行到下一個yield語句:yield 'yield2',這個語句會返回'yield2',它會作為$g->send('ret1')的返回值,所以函數外第二個var_dump會輸出'yield2'。

最后再次調用send函數,這次傳入的參數為字符串'ret2',跟上面一樣,Generator對象當前位置的元素是在gen函數的第二個yield上,所以’ret2'會被傳遞給第二個yield表達式,也就是作為(yield 'yield2')中的yield的值,並且會被賦值給$ret變量,然后gen函數恢復執行,它會執行gen函數中的最后一個var_dump,此時對Generator對象$g的遍歷也結束了,第二個send函數的返回值為NULL,這也是函數外的最后一個var_dump的輸出。

讀了這么一段分析以后,你現在最大的困惑是什么呢?

我最大的困惑是為什么同一個yied關鍵字,它既是語句,又是表達式,而且這兩種情況是同時存在的:

  1. 對於所有在generator函數中出現的yield,首先它都是語句,而跟在yield后面的任何表達式的值將作為調用generator函數的返回值,如果yield后面沒有任何表達式(變量、常量都是表達式),那么它會返回NULL,這一點跟return語句一致。
  2. yield也是表達式,它的值就是send函數傳過來的值(相當於一個特殊變量,只不過賦值是通過send函數進行的)。只要調用send方法,並且Generator對象的迭代並未終結,那么當前位置的yield就會得到send方法傳遞過來的值,這跟generator函數有沒有把這個值賦值給某個變量沒有任何關系。

從上面兩點我們就可以看出,任何時候yield關鍵詞都即是語句——可以為generator函數返回值,也是表達式——可以接收Generator對象發過來的值。

-------------------------分界線:上面是摘抄的,下面是自己的疑問---------------------------

講的很好。但有點疑問就是:

在執行完gen中的var_dump之后,generator應該終止啊。但是,為什么卻又恢復了,繼續執行下一條yield語句呢。 我猜是因為當yield作為表達式的時候,generator並沒有進行迭代。只有yield被當做了語句執行之后,generator才會終止吧。


------------再次補充:關於yield既是表達式又是語句的理解--------

<?php
    function gen() {
        for($i=1;$i<=100;$i++) {
            $cmd = (yield $i);
            if($cmd=='stop') {
                return;
            }
        }
    }
    $gen = gen();
    $i=0;
    foreach($gen as $item) {
        echo $item."\n";
        if($i>=10) {
            $gen->send('stop');
        }
        $i++;
    }
?>

 

這是個很好地例子:
1、yield作為語句(類似return語句),會返回$i給調用者。
2、yield作為表達式。獲取send函數傳遞值,賦值給$cmd。
3、實現Generator對象和generator函數的通信。這個很重要。應該能實現很多generator的交互.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM