ใน PHP: อะไรคือความแตกต่างระหว่าง "ผลตอบแทน", "ผลผลิต", "ผลผลิตจาก" และการผสมทั้งผลผลิตและผลตอบแทนในฟังก์ชั่นเดียวกัน?


10

ความแตกต่างระหว่างreturnและyieldดูเหมือนชัดเจนจนกระทั่งฉันพบว่ามีyield fromและความเป็นไปได้ที่จะรวมทั้งreturnและyieldในฟังก์ชั่นเดียวกัน!

ความเข้าใจของฉันreturnคือว่าทุกอย่างหลังจากไม่ได้ดำเนินการใช่มั้ย

อย่างไรก็ตาม:

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

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

ผลิต: "123"

แต่ต่อไปนี้:

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

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

ไม่ทำอะไรเลย! นั่นหมายความว่ามีการผลิตผลหรือไม่

นี่เป็นข้อบกพร่องหรือไม่?


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

คำตอบ:


10

Return

เพียงให้คุณค่าที่เป็นเอกลักษณ์แก่ผู้โทร

Yield

เปลี่ยนฟังก์ชั่น / วิธีการปัจจุบันเพื่อคืนค่า a Generatorซึ่งจะสร้างมากกว่าค่าที่ไม่ซ้ำกัน: ทุกครั้งyieldจะถูกทริกเกอร์มันจะให้ค่ากับผู้โทรครั้งละหนึ่งครั้งโดยใช้foreachลูป

Yield + Return

เครื่องกำเนิดไฟฟ้านอกจากจะสร้างค่าแล้วยังสามารถให้ค่าส่งคืนที่ไม่ซ้ำกันได้อีกด้วย ค่านั้นจะไม่เป็นส่วนหนึ่งของลูปรอบตัวสร้างมันจะต้องเข้าถึงได้โดยใช้Generator::getReturn()วิธีการ

Return + Yield

นี่อาจถูกมองว่าเป็นข้อผิดพลาดอย่างไรก็ตามไม่ใช่

พวกเขาเป็นสองขั้นตอน:

  1. จากรหัส bytecode : ในช่วงนี้ที่generate()ฟังก์ชั่นก็เห็นจะมีคำหลักดังนั้นจึงเป็นเรื่องที่ทำเครื่องหมายเป็นผลิตyieldGenerator
  2. การประหารชีวิต : เนื่องจากสิ่งที่returnเกิดขึ้นก่อนหน้าyieldเครื่องกำเนิดไม่มีโอกาสสร้างมูลค่าใด ๆ อย่างไรก็ตามอาร์เรย์สามารถเรียกดูได้ด้วย[1, 2, 3]Generator::getReturn()

ตัวอย่างคำอธิบายประกอบแบบสมบูรณ์:

// 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
ตัวอย่างสุดท้ายการคืนค่า "สิ้นสุด" การดำเนินการของฟังก์ชั่นนั่นคือรหัสไม่สามารถใช้ yeld ได้
Rodrigo Jarouche

5

จากเอกสารประกอบ :

ฟังก์ชั่นใด ๆ ที่มีyieldเป็นฟังก์ชั่นเครื่องกำเนิดไฟฟ้า

ดังนั้นมันจึงไม่สำคัญว่าyieldจะถูกดำเนินการหรือไม่ parser มองเห็นที่ใดที่หนึ่งในนิยามฟังก์ชันและเปลี่ยนเป็นเครื่องกำเนิดไฟฟ้า

หากฟังก์ชั่นไม่เคยประมวลผลyieldคำสั่งเครื่องกำเนิดไฟฟ้าจะไม่สร้างค่าใด ๆ ค่าที่ส่งคืนโดยreturnจะถูกละเว้นเมื่อคุณพยายามใช้ผลลัพธ์ เอกสารกล่าวว่า:

หมายเหตุ:
ใน PHP 5 ตัวสร้างไม่สามารถส่งคืนค่าได้การทำเช่นนั้นจะส่งผลให้เกิดข้อผิดพลาดในการคอมไพล์ returnคำสั่งที่ว่างเปล่าคือไวยากรณ์ที่ถูกต้องภายในตัวสร้างและมันจะยุติตัวสร้าง ตั้งแต่ PHP 7.0 กำเนิดสามารถกลับค่าซึ่งสามารถเรียกดูได้โดยใช้เครื่องกำเนิดไฟฟ้า :: getReturn ()

ดังนั้นคุณสามารถทำได้:

$gen = generate();
foreach ($gen as $value) {
    echo $value;
}
print_r($gen->getReturn());
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.