In PHP: Was ist der Unterschied zwischen "Rendite", "Ausbeute", "Ausbeute von" und dem Mischen von Ausbeute und Rendite in derselben Funktion?

10

Der Unterschied zwischen returnund yieldschien klar zu sein, bis ich herausfand, dass es auch yield fromdie Möglichkeit gab, beide returnund yieldin derselben Funktion zu kombinieren !

Mein Verständnis von returnwar , dass alles nach wurde nicht ausgeführt, nicht wahr?

Jedoch:

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

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

Produziert: "123"

Aber folgendes:

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

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

Produziert nichts! Das heißt also, dass die Rendite ausgeführt wird?

Ist das ein Fehler?


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

Antworten:

10

Return

Gibt dem Anrufer einfach einen eindeutigen Wert zurück.

Yield

Transformieren Sie die aktuelle Funktion / Methode, um a zurückzugeben Generator, das mehr als einen eindeutigen Wert erzeugt: Jedes Mal yield, wenn es ausgelöst wird, gibt es den Wert nacheinander an den Aufrufer weiter, wobei traditionell eine foreachSchleife verwendet wird.

Yield + Return

Generatoren können zusätzlich zum Generieren von Werten auch einen eindeutigen Rückgabewert bereitstellen. Dieser Wert ist nicht Teil der Schleife um den Generator, sondern muss mit der Generator::getReturn()Methode aufgerufen werden .

Return + Yield

Dies könnte als Fehler angesehen werden, ist es aber nicht.

Sie sind zwei Phasen:

  1. Von Code zu Bytecode : Während dieser Phase enthält die generate()Funktion das yieldSchlüsselwort und wird daher als a erzeugend markiertGenerator .
  2. Ausführung : Da returndies zufällig vor dem liegt yield, hat der Generator keine Chance, einen Wert zu erzeugen. Das [1, 2, 3]Array kann jedoch mit abgerufen werden Generator::getReturn().

Ein vollständig kommentiertes Beispiel:

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

quelle
1
Im letzten Beispiel "beendet" die Rückgabe die Funktionsausführung, dh der Code erreicht das yeld nicht.
Rodrigo Jarouche
5

Aus der Dokumentation :

Jede Funktion, die enthält, yieldist eine Generatorfunktion.

Es spielt also keine Rolle, ob das yieldausgeführt wird, der Parser sieht es irgendwo in der Funktionsdefinition und verwandelt es in einen Generator.

Wenn die Funktion die yieldAnweisung niemals ausführt , erzeugt der Generator keine Werte. Der von zurückgegebene Wert returnwird ignoriert, wenn Sie versuchen, das Ergebnis zu verwenden. Die Dokumentation sagt:

Hinweis:
In PHP 5 konnte ein Generator keinen Wert zurückgeben. Dies würde zu einem Kompilierungsfehler führen. Eine leere returnAnweisung war eine gültige Syntax innerhalb eines Generators und würde den Generator beenden. Seit PHP 7.0 kann ein Generator Werte zurückgeben, die mit Generator :: getReturn () abgerufen werden können .

So könnten Sie tun:

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