W PHP: jaka jest różnica między „zwrotem”, „wydajnością”, „wydajnością z” i mieszaniem zarówno wydajności jak i zwrotu w tej samej funkcji?


10

Różnica pomiędzy returni yieldwydawało się oczywiste, dopóki nie zorientowali się tam także yield fromi możliwość łączenia obu returni yieldw tej samej funkcji!

Rozumiałem, returnże wszystko po tym nie zostało wykonane, prawda?

Jednak:

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

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

Produkuje: „123”

Ale następujące:

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

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

Nic nie produkuje! Czy to oznacza, że ​​dochód jest realizowany?

Czy to błąd?


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

Odpowiedzi:


10

Return

Po prostu zwraca dzwoniącemu wyjątkową wartość.

Yield

Przekształć bieżącą funkcję / metodę, aby zwrócić a Generator, która da więcej niż unikalną wartość: za każdym razem, gdy yieldjest wyzwalana, daje wartość wywołującemu, pojedynczo, tradycyjnie za pomocą foreachpętli.

Yield + Return

Generatory, oprócz generowania wartości, mogą również zapewniać unikalną zwracaną wartość. Ta wartość nie będzie częścią pętli wokół generatora, należy uzyskać do niej dostęp przy użyciu Generator::getReturn()metody.

Return + Yield

Może to być postrzegane jako błąd, ale tak nie jest.

Są to dwie fazy:

  1. Od kodu do kodu bajtowego : podczas tej fazy generate()funkcja zawiera yieldsłowo kluczowe, dlatego jest oznaczona jako wytwarzająca Generator.
  2. Wykonanie : Ponieważ returnzdarza się przed yield, generator nie ma szansy wygenerować żadnej wartości. Jednakże [1, 2, 3]macierz może być pobrana z Generator::getReturn().

Kompletny przykład z adnotacjami:

// 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
Ostatni przykład, return „kończy” wykonanie funkcji, to znaczy kod nie osiąga yeld.
Rodrigo Jarouche

5

Z dokumentacji :

Każda funkcja zawierająca yieldjest funkcją generatora.

Nie ma więc znaczenia, czy yieldjest ono wykonywane, parser widzi je gdzieś w definicji funkcji i zamienia w generator.

Jeśli funkcja nigdy nie wykonuje yieldinstrukcji, generator nie generuje żadnych wartości. Zwracana wartość returnjest ignorowana podczas próby użycia wyniku. Dokumentacja mówi:

Uwaga:
W PHP 5 generator nie mógł zwrócić wartości: spowodowałoby to błąd kompilacji. Pusta returninstrukcja była poprawną składnią w generatorze i zakończyła działanie generatora. Od PHP 7.0 generator może zwracać wartości, które można odzyskać za pomocą Generator :: getReturn () .

Więc możesz zrobić:

$gen = generate();
foreach ($gen as $value) {
    echo $value;
}
print_r($gen->getReturn());
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.