Dalam PHP: apa perbedaan antara "kembali", "hasil", "hasil dari" dan pencampuran keduanya hasil dan kembali dalam fungsi yang sama?

10

Perbedaan antara returndan yieldtampak jelas sampai saya menemukan ada juga yield fromdan kemungkinan untuk menggabungkan keduanya returndan yielddalam fungsi yang sama!

Pemahaman saya returnadalah bahwa semuanya setelah itu tidak dieksekusi, kan?

Namun:

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

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

Menghasilkan: "123"

Namun berikut ini:

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

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

Tidak menghasilkan apa-apa! Jadi itu berarti hasil dieksekusi?

Apakah ini bug?


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

Jawaban:

10

Return

Cukup memberikan kembali nilai unik kepada pemanggil.

Yield

Ubah fungsi / metode saat ini untuk mengembalikan a Generator, yang akan menghasilkan lebih dari nilai unik: setiap kali yielddipicu, ini memberikan nilai kepada penelepon, satu per satu, secara tradisional menggunakan foreachloop.

Yield + Return

Generator, selain menghasilkan nilai, juga dapat memberikan nilai pengembalian yang unik. Nilai itu tidak akan menjadi bagian dari perulangan di sekitar generator, itu harus diakses menggunakan Generator::getReturn()metode.

Return + Yield

Ini bisa dilihat sebagai bug, namun tidak.

Mereka adalah dua fase:

  1. Dari kode ke bytecode : selama fase ini, generate()fungsi terlihat mengandung yieldkata kunci, karena itu ditandai sebagai menghasilkan a Generator.
  2. Eksekusi : Karena returnkebetulan sebelum yield, generator tidak memiliki kesempatan untuk menghasilkan nilai apa pun. Namun, [1, 2, 3]array dapat diambil dengan Generator::getReturn().

Contoh beranotasi lengkap:

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

sumber
1
Contoh terakhir, pengembalian "mengakhiri" eksekusi fungsi, yaitu, kode tidak mencapai yeld.
Rodrigo Jarouche
5

Dari dokumentasi :

Setiap fungsi yang mengandung yieldadalah fungsi generator.

Jadi tidak masalah apakah yielddieksekusi, parser melihatnya di suatu tempat dalam definisi fungsi dan mengubahnya menjadi generator.

Jika fungsi tidak pernah menjalankan yieldpernyataan, maka generator tidak menghasilkan nilai apa pun. Nilai yang dikembalikan oleh returndiabaikan ketika Anda mencoba menggunakan hasilnya. Dokumentasi mengatakan:

Catatan:
Di PHP 5, generator tidak dapat mengembalikan nilai: melakukannya akan menghasilkan kesalahan kompilasi. returnPernyataan kosong adalah sintaks yang valid di dalam generator dan itu akan menghentikan generator. Sejak PHP 7.0, generator dapat mengembalikan nilai, yang dapat diambil menggunakan Generator :: getReturn () .

Jadi Anda bisa melakukan:

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