wake-up-neo.com

Was bedeutet Ertrag in PHP?

Ich bin kürzlich über diesen Code gestolpert:

function xrange($min, $max) 
{
    for ($i = $min; $i <= $max; $i++) {
        yield $i;
    }
}

Ich habe dieses yield Schlüsselwort noch nie gesehen. Ich versuche den Code auszuführen, den ich bekomme

Parse-Fehler: Syntaxfehler, unerwarteter T_VARIABLE in Zeile x

Was ist das yield Schlüsselwort? Ist es überhaupt gültiges PHP? Und wenn ja, wie verwende ich es?

204
Gordon

Was ist yield?

Das Schlüsselwort yieldgibt Daten von einer Generatorfunktion zurück:

Das Herzstück einer Generatorfunktion ist das Schlüsselwort yield. In der einfachsten Form ähnelt eine yield-Anweisung einer return-Anweisung. Statt die Ausführung der Funktion zu stoppen und zurückzugeben, liefert yield einen Wert für den Code, der den Generator durchläuft, und unterbricht die Ausführung der generator-Funktion.

Was ist eine Generatorfunktion?

Eine Generatorfunktion ist effektiv eine kompaktere und effizientere Möglichkeit, einen Iterator zu schreiben. Hier können Sie eine Funktion definieren (Ihre xrange), die berechnet und zurückgibt Werte while Sie sind drüberschleifen :

foreach (xrange(1, 10) as $key => $value) {
    echo "$key => $value", PHP_EOL;
}

Dies würde die folgende Ausgabe erzeugen:

0 => 1
1 => 2
…
9 => 10

Sie können auch den $key In foreach steuern, indem Sie verwenden

yield $someKey => $someValue;

In der Generatorfunktion ist $someKey Das, was Sie wollen, wobei $key Und $someValue Der Wert in $val Sind. Im Beispiel der Frage ist das $i.

Was ist der Unterschied zu normalen Funktionen?

Nun fragen Sie sich vielleicht, warum wir nicht einfach die native range -Funktion von PHP verwenden, um diese Ausgabe zu erzielen. Und genau du bist. Die Ausgabe wäre die gleiche. Der Unterschied ist, wie wir dorthin gekommen sind.

Wenn wir range PHP verwenden, werden wir es ausführen, das gesamte Array von Zahlen im Speicher erstellen und return das gesamtes Array zum foreach Schleife, die daraufhin die Werte ausgibt. Mit anderen Worten, die Funktion foreach wird auf dem Array selbst ausgeführt. Die Funktion range und die Funktion foreach "sprechen" nur einmal. Stellen Sie sich vor, Sie bekommen ein Paket per Post. Der Zusteller gibt Ihnen das Paket und geht. Und dann packen Sie das gesamte Paket aus und nehmen heraus, was sich dort befindet.

Wenn wir die Generatorfunktion verwenden, tritt PHP in die Funktion ein und führt sie aus, bis sie entweder das Ende oder ein yield - Schlüsselwort erfüllt. Wenn sie ein yield gibt dann den jeweils aktuellen Wert an die äußere Schleife zurück, kehrt dann zur Generatorfunktion zurück und fährt dort fort, wo er ausgegeben wurde. Da Ihr xrange ein for Schleife, die ausgeführt und ausgegeben wird, bis $max erreicht wurde. Stellen Sie sich das wie den foreach und den Generator vor, der Ping-Pong spielt.

Warum brauche ich das?

Offensichtlich können Generatoren verwendet werden, um Speichergrenzen zu umgehen. Abhängig von Ihrer Umgebung wird das Ausführen einer range(1, 1000000) Ihr Skript zum Scheitern bringen, wohingegen dasselbe mit einem Generator gut funktioniert. Oder wie Wikipedia es ausdrückt:

Da Generatoren ihre Ertragswerte nur bei Bedarf berechnen, sind sie nützlich, um Sequenzen darzustellen, die teuer oder unmöglich auf einmal zu berechnen wären. Diese umfassen z.B. Unendliche Sequenzen und Live-Datenströme.

Generatoren sollen auch ziemlich schnell sein. Denken Sie jedoch daran, dass wir, wenn wir über schnell sprechen, in der Regel in sehr kleinen Zahlen sprechen. Bevor Sie jetzt loslegen und Ihren gesamten Code ändern, um Generatoren zu verwenden, sollten Sie einen Benchmark durchführen, um festzustellen, wo dies sinnvoll ist.

Ein weiterer Anwendungsfall für Generatoren sind asynchrone Coroutinen. Das Schlüsselwort yield gibt nicht nur Werte zurück, sondern akzeptiert sie auch. Einzelheiten dazu finden Sie in den beiden unten verlinkten hervorragenden Blog-Posts.

Seit wann kann ich yield verwenden?

Generatoren wurden in PHP 5.5 eingeführt. Der Versuch, yield vor dieser Version zu verwenden, führt je nach Code, der auf das Schlüsselwort folgt, zu verschiedenen Analysefehlern. Wenn Sie also einen Analysefehler von diesem Code bekommen, aktualisieren Sie Ihr PHP.

Quellen und weiterführende Literatur:

301
Gordon

Diese Funktion mit Ausbeute:

function a($items) {
    foreach ($items as $item) {
        yield $item + 1;
    }
}

ist fast das gleiche wie dieses ohne:

function b($items) {
    $result = [];
    foreach ($items as $item) {
        $result[] = $item + 1;
    }
    return $result;
}

Mit nur einem Unterschied, dass a() ein generator und b() nur ein einfaches Array zurückgibt. Sie können auf beiden iterieren.

Außerdem weist der erste keine vollständige Anordnung zu und ist daher weniger speicherintensiv.

27
tsusanka

einfaches Beispiel

<?php
echo '#start main# ';
function a(){
    echo '{start[';
    for($i=1; $i<=9; $i++)
        yield $i;
    echo ']end} ';
}
foreach(a() as $v)
    echo $v.',';
echo '#end main#';
?>

Ausgabe

#start main# {start[1,2,3,4,5,6,7,8,9,]end} #end main#
20
Think Big

yield Schlüsselwort dient zur Definition von "Generatoren" in PHP 5.5. Ok, was ist dann ein Generator ?

Von php.net:

Generatoren bieten eine einfache Möglichkeit, einfache Iteratoren zu implementieren, ohne den Aufwand oder die Komplexität der Implementierung einer Klasse, die die Iterator-Schnittstelle implementiert.

Mit einem Generator können Sie Code schreiben, der mit foreach einen Datensatz durchläuft, ohne ein Array im Speicher erstellen zu müssen. Dies kann dazu führen, dass Sie ein Speicherlimit überschreiten oder eine erhebliche Verarbeitungszeit für die Generierung benötigen. Stattdessen können Sie eine Generatorfunktion schreiben, die mit einer normalen Funktion identisch ist, mit der Ausnahme, dass ein Generator nicht nur einmal zurückgegeben wird, sondern auch so oft liefert, wie es erforderlich ist, um die Werte bereitzustellen, über die iteriert werden soll.

Von diesem Ort: Generatoren = Generatoren, andere Funktionen (nur einfache Funktionen) = Funktionen.

Sie sind also nützlich, wenn:

  • Sie müssen die Dinge einfach (oder einfach) machen;

    generator ist wirklich viel einfacher als die Implementierung der Iterator-Schnittstelle. Andererseits sind Generatoren natürlich weniger funktionsfähig. vergleiche sie .

  • Sie müssen große Datenmengen erzeugen - Speicher sparen;

    um Speicherplatz zu sparen, können wir einfach die benötigten Daten über Funktionen für jede Schleifeniteration generieren und nach der Iteration Garbage verwenden. Hier geht es also vor allem um eindeutigen Code und wahrscheinlich um Leistung. sehen Sie, was für Ihre Bedürfnisse besser ist.

  • Sie müssen eine Sequenz generieren, die von Zwischenwerten abhängt;

    dies erweitert den vorherigen Gedanken. Generatoren können es im Vergleich zu Funktionen einfacher machen. überprüfen Sie Fibonacci-Beispiel und versuchen Sie, eine Sequenz ohne Generator zu erstellen. Auch Generatoren können in diesem Fall schneller arbeiten, zumindest weil Zwischenwerte in lokalen Variablen gespeichert werden.

  • Sie müssen die Leistung verbessern.

    sie können in einigen Fällen schneller arbeiten als sie funktionieren (siehe vorheriger Nutzen).

20
QArea

Mit yield können Sie auf einfache Weise die Haltepunkte zwischen mehreren Aufgaben in einer einzigen Funktion beschreiben. Das ist alles, es gibt nichts Besonderes.

$closure = function ($injected1, $injected2, ...){
    $returned = array();
    //task1 on $injected1
    $returned[] = $returned1;
//I need a breakpoint here!!!!!!!!!!!!!!!!!!!!!!!!!
    //task2 on $injected2
    $returned[] = $returned2;
    //...
    return $returned;
};
$returned = $closure($injected1, $injected2, ...);

Wenn task1 und task2 eng miteinander verbunden sind, Sie jedoch einen Haltepunkt zwischen ihnen benötigen, um etwas anderes zu tun:

  • freier Speicher zwischen der Verarbeitung von Datenbankzeilen
  • führen Sie andere Tasks aus, die eine Abhängigkeit von der nächsten Task bieten, für die jedoch keine Beziehung zum aktuellen Code besteht
  • asynchrone Anrufe tätigen und auf die Ergebnisse warten
  • und so weiter ...

dann sind Generatoren die beste Lösung, da Sie Ihren Code nicht in viele Closures aufteilen oder mit anderem Code mischen oder Rückrufe usw. verwenden müssen. Sie verwenden einfach yield, um einen Haltepunkt hinzuzufügen. und Sie können von diesem Haltepunkt aus fortfahren, wenn Sie bereit sind.

Haltepunkt ohne Generatoren hinzufügen:

$closure1 = function ($injected1){
    //task1 on $injected1
    return $returned1;
};
$closure2 = function ($injected2){
    //task2 on $injected2
    return $returned1;
};
//...
$returned1 = $closure1($injected1);
//breakpoint between task1 and task2
$returned2 = $closure2($injected2);
//...

Haltepunkt mit Generatoren hinzufügen

$closure = function (){
    $injected1 = yield;
    //task1 on $injected1
    $injected2 = (yield($returned1));
    //task2 on $injected2
    $injected3 = (yield($returned2));
    //...
    yield($returnedN);
};
$generator = $closure();
$returned1 = $generator->send($injected1);
//breakpoint between task1 and task2
$returned2 = $generator->send($injected2);
//...
$returnedN = $generator->send($injectedN);

Hinweis: Es ist leicht, Fehler mit Generatoren zu machen. Schreiben Sie daher immer Unit-Tests, bevor Sie sie implementieren!Hinweis 2: Die Verwendung von Generatoren in einer Endlosschleife ist wie das Schreiben eines Verschlusses mit unendlicher Länge. ..

7
inf3rno

Ein interessanter Aspekt, der hier diskutiert werden sollte, ist nter Bezugnahme. Jedes Mal, wenn wir einen Parameter so ändern müssen, dass er außerhalb der Funktion angezeigt wird, müssen wir diesen Parameter als Referenz übergeben. Um dies auf Generatoren anzuwenden, stellen wir dem Namen des Generators und der in der Iteration verwendeten Variablen einfach ein kaufmännisches Und & Voran:

 <?php 
 /**
 * Yields by reference.
 * @param int $from
 */
function &counter($from) {
    while ($from > 0) {
        yield $from;
    }
}

foreach (counter(100) as &$value) {
    $value--;
    echo $value . '...';
}

// Output: 99...98...97...96...95...

Das obige Beispiel zeigt, wie durch Ändern der iterierten Werte in der foreach -Schleife die Variable $from Im Generator geändert wird. Dies liegt daran, dass $from Aufgrund des kaufmännischen Und-Zeichens vor dem Namen des Generators als Referenz angegeben ist. Aus diesem Grund ist die Variable $value In der foreach -Schleife eine Referenz auf die Variable $from In der Generatorfunktion.

1
bodi0