【问题标题】:In PHP: what is the difference between "return", "yield", "yield from" and mixing both yield and return in same function?在 PHP 中:“return”、“yield”、“yield from”和在同一个函数中混合 yield 和 return 有什么区别?
【发布时间】:2020-05-30 14:32:08
【问题描述】:

returnyield 之间的区别似乎很明显,直到我发现还有 yield from 并且可以将 returnyield 组合在同一个函数中!

我对@9​​87654326@ 的理解是,之后的一切都没有被执行,对吧?

但是:

function generate(): iterable {
    return [1, 2, 3];
}

foreach (generate() as $value) {
    echo $value;
}

产生:“123”

但是如下:

function generate(): iterable {
    return [1, 2, 3];
    yield;
}

foreach (generate() as $value) {
    echo $value;
}

什么都不生产!那么这意味着yield被执行了吗?

这是一个错误吗?

【问题讨论】:

  • var_dump(generate()->GetReturn());

标签: php return generator yield


【解决方案1】:

Return

简单地将唯一值返回给调用者。

Yield

转换当前函数/方法以返回一个Generator,这将产生多个唯一值:每次触发yield,它都会将值提供给调用者,一次一个,传统上使用@ 987654328@ 循环。

Yield + Return

生成器除了生成值之外,还可以提供唯一的返回值。该值不会成为生成器循环的一部分,必须使用Generator::getReturn() 方法访问它。

Return + Yield

这可能被视为错误,但事实并非如此。

它们是两个阶段:

  1. 从代码到字节码:在这个阶段,generate() 函数被视为包含yield 关键字,因此它被标记为产生Generator
  2. 执行:因为return 恰好在yield 之前,所以生成器没有机会产生任何值。但是,[1, 2, 3] 数组可以使用Generator::getReturn() 检索。

一个完整的注释示例:

// Generate integers 1 and 2
function generateIntegers1And2(): Generator {
    yield 1;                                  // <--+   <--+   <--+
    yield 2;                                  //  <-+    <-+    <-+
}                                             //    |      |      |
                                              //    |      |      |
foreach (generateIntegers1And2() as $value) { //    |      |      |
    var_dump($value); // Shows 1, then 2          ->*      |      |
}                                                       // |      |
                                                        // |      |
function generateOuterYield(): Generator {              // |      |
    // Yields the generator *itself* returned by           |      |
    // generateIntegers1And2() not the actual values       |      |
    // generated by it.                                    |      |
    // This means we are producing here a generator        |      |
    // of generator of integers.                           |      |
    yield generateIntegers1And2();          // <-+         |      |
}                                             // |         |      |
                                              // |         |      |
foreach (generateOuterYield() as $value) {    // |         |      |
    var_dump($value);                       // ->*         |      |
    // The two levels of imbrication means we have         |      |
    // to loop once more to actually consume               |      |
    // generateIntegers1And2                               |      |
    foreach ($value as $val) {                          // |      |
        var_dump($val); // Shows 1, then 2               ->*      |
    }                                                          // |
}                                                              // |
                                                               // |
// A generator can just be returned as-is:                        |
function generateOuterReturn(): Generator {                    // |
    return generateIntegers1And2();                            // |
}                                                              // |
                                                               // |
// it doesn't change the way it is consumed                       |
foreach (generateOuterReturn() as $value) {                    // |
    var_dump($value); // Shows 1, then 2                          |
}                                                              // |
                                                               // |
function generateOuterYieldFrom(): Generator {                 // |
    // First yield values generated by generateIntegers1And2()    |
    yield from generateIntegers1And2();                        // *<---+
    // then yield integers 3                                           |
    yield 3;                                                     // <--+
    // and 4                                                           |
    yield 4;                                                     //  <-+
}                                                                //    |
                                                                 //    |
foreach (generateOuterYieldFrom() as $value) {                   //    |
    var_dump($value); // Shows 1, 2, 3 and 4                         ->*
}

function generateIntegers56AndReturn(): Generator {
    yield 5;                                                  // <---+
    yield 6;                                                  //  <--+
                                                              //     |
    return ["five", "six"];                       // <--+            |
}                                                 //    |            |
                                                  //    |            |
$gen = generateIntegers56AndReturn();             //    |            |
                                                  //    |            |
// Consume the values **yielded** by                    |            |
// generateIntegers56AndReturn()                        |            |
foreach ($gen as $value) {                        //    |            |
    var_dump($value); // Shows 5, then 6                |          ->*
}                                                 //    |
                                                  //    |
// Access the value **returned** by the generator       |
var_dump($gen->getReturn());                      //  ->*

function wtf(): Generator {
    return ["W", "T", "F", "!"];
    // Without the following line, PHP would complain with a TypeError:
    // Return value of wtf() must be an instance of Generator, array returned.
    // The presence of a yield keyword anywhere inside the function makes it a Generator.
    // However, since we return *before* reaching any *yield*, 42 is never yielded.
    // This is empty generator!
    yield 42;
}

$gen = wtf();

// This foreach loop is not entered!
foreach ($gen as $value) {
    var_dump($value);
}

// However, we can loop on the array *returned* by wtf():
foreach ($gen->getReturn() as $value) {
    echo $value; // Will print: WTF!
}

【讨论】:

  • 最后一个例子,return“结束”了函数的执行,也就是代码没有实现yeld。
【解决方案2】:

来自documentation

任何包含yield 的函数都是生成器函数。

所以yield是否被执行并不重要,解析器在函数定义的某个地方看到它并将它变成一个生成器。

如果函数从不执行yield 语句,则生成器不会产生任何值。当您尝试使用结果时,return 返回的值将被忽略。文档说:

注意:
在 PHP 5 中,生成器不能返回值:这样做会导致编译错误。一个空的return 语句是生成器中的有效语法,它将终止生成器。从 PHP 7.0 开始,生成器可以返回值,可以使用 Generator::getReturn() 检索。

所以你可以这样做:

$gen = generate();
foreach ($gen as $value) {
    echo $value;
}
print_r($gen->getReturn());

【讨论】:

    猜你喜欢
    • 2017-01-09
    • 2017-11-26
    • 2016-06-01
    • 2016-09-08
    • 2018-01-19
    • 2021-09-12
    • 2012-03-26
    • 2020-10-23
    相关资源
    最近更新 更多