In PHP: qual è la differenza tra “rendimento”, “rendimento”, “rendimento da” e mescolare sia rendimento che rendimento nella stessa funzione?


10

La differenza tra returne yieldsembrava chiara fino a quando ho capito che c'era anche yield fromla possibilità di combinare entrambi returneyield nella stessa funzione!

La mia comprensione returnera che tutto ciò che non era dopo eseguito, giusto?

Però:

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

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

Produce: "123"

Ma quanto segue:

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

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

Non produce niente! Quindi questo significa che la resa viene eseguita?

è un insetto?


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

Risposte:


10

Return

Restituisce semplicemente un valore unico al chiamante.

Yield

Trasforma la funzione / metodo corrente per restituire a Generator, che produrrà più di un valore univoco: ogni volta che yieldviene attivato, fornisce il valore al chiamante, uno alla volta, usando tradizionalmente un foreachciclo.

Yield + Return

I generatori, oltre a generare valori, possono anche fornire un valore restituito univoco. Tale valore non farà parte del ciclo attorno al generatore, ma è necessario accedervi usando il Generator::getReturn()metodo.

Return + Yield

Questo potrebbe essere visto come un bug, tuttavia, non lo è.

Sono due fasi:

  1. Dal codice al bytecode : durante questa fase, la generate()funzione contiene la yieldparola chiave, pertanto viene contrassegnata come producendo a Generator.
  2. Esecuzione : poiché returnaccade prima yield, il generatore non ha la possibilità di produrre alcun valore. Tuttavia, l' [1, 2, 3]array può essere recuperato con Generator::getReturn().

Un esempio annotato completo:

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

1
Nell'ultimo esempio, il ritorno "termina" l'esecuzione della funzione, ovvero il codice non raggiunge lo yeld.
Rodrigo Jarouche,

5

Dalla documentazione :

Qualsiasi funzione contenente yieldè una funzione del generatore.

Quindi non importa se yieldviene eseguito, il parser lo vede da qualche parte nella definizione della funzione e lo trasforma in un generatore.

Se la funzione non esegue mai l' yieldistruzione, il generatore non produce alcun valore. Il valore restituito da returnviene ignorato quando si tenta di utilizzare il risultato. La documentazione dice:

Nota:
in PHP 5, un generatore non può restituire un valore: ciò comporterebbe un errore di compilazione. returnUn'affermazione vuota era una sintassi valida all'interno di un generatore e avrebbe terminato il generatore. A partire da PHP 7.0, un generatore può restituire valori, che possono essere recuperati usando Generator :: getReturn () .

Quindi potresti fare:

$gen = generate();
foreach ($gen as $value) {
    echo $value;
}
print_r($gen->getReturn());
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.