En PHP: ¿cuál es la diferencia entre "retorno", "rendimiento", "rendimiento de" y la mezcla de rendimiento y rendimiento en la misma función?

10

¡La diferencia entre returny yieldparecía clara hasta que descubrí que también existía yield fromy la posibilidad de combinar ambos returny yielden la misma función!

Mi comprensión de returnfue que todo después no fue ejecutado, ¿verdad?

Sin embargo:

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

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

Produce: "123"

Pero lo siguiente:

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

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

¡No produce nada! Entonces, ¿eso significa que el rendimiento se ejecuta?

¿Es esto un error?


fuente
1
var_dump(generate()->GetReturn());
AbraCadaver

Respuestas:

10

Return

Simplemente devuelve un valor único a la persona que llama.

Yield

Transforme la función / método actual para devolver a Generator, lo que producirá más que un valor único: cada vez que yieldse activa, le da el valor a la persona que llama, uno a la vez, tradicionalmente usando un foreachbucle.

Yield + Return

Los generadores, además de generar valores, también pueden proporcionar un valor devuelto único. Ese valor no será parte del bucle alrededor del generador, debe accederse a él utilizando el Generator::getReturn()método

Return + Yield

Esto podría verse como un error, sin embargo, no lo es.

Son dos fases:

  1. Del código al bytecode : durante esta fase, generate()se ve que la función contiene la yieldpalabra clave, por lo tanto se marca como produciendo a Generator.
  2. Ejecución : Debido a que returnsucede antes que el yield, el generador no tiene la posibilidad de producir ningún valor. Sin embargo, la [1, 2, 3]matriz se puede recuperar con Generator::getReturn().

Un ejemplo completo anotado:

// 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!
}

fuente
1
El último ejemplo, el retorno "finaliza" la ejecución de la función, es decir, el código no alcanza el rendimiento.
Rodrigo Jarouche
5

De la documentación :

Cualquier función que contenga yieldes una función generadora.

Por lo tanto, no importa si yieldse ejecuta, el analizador lo ve en algún lugar de la definición de la función y lo convierte en un generador.

Si la función nunca ejecuta la yielddeclaración, entonces el generador no produce ningún valor. El valor devuelto por returnse ignora cuando intenta utilizar el resultado. La documentación dice:

Nota:
en PHP 5, un generador no podía devolver un valor: hacerlo provocaría un error de compilación. Una returndeclaración vacía era una sintaxis válida dentro de un generador y terminaría el generador. Desde PHP 7.0, un generador puede devolver valores, que pueden recuperarse usando Generator :: getReturn () .

Entonces podrías hacer:

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