Que signifie rendement en PHP?

j'ai récemment trébuché sur ce code:

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

Je n'ai jamais vu ce mot-clé yield avant. Essayer d'exécuter le code que je reçois

erreur D'analyse: erreur de syntaxe, inattendu T_VARIABLE sur la ligne x

alors, qu'est-ce que ce mot-clé yield ? Est-il même valide PHP? Et si elle l'est, comment puis-je l'utiliser?

173
demandé sur k0pernikus 2013-07-05 11:53:07

5 réponses

Qu'est-ce que yield ?

Le yield mot-clé retourne des données à partir d'un générateur de fonction:

le coeur d'une fonction de générateur est le mot-clé de rendement. Dans sa forme la plus simple, une instruction yield ressemble beaucoup à une instruction return, sauf qu'au lieu d'arrêter l'exécution de la fonction et de retourner, yield fournit une valeur au code en boucle au-dessus du générateur et des pauses exécution de la fonction génératrice.

Qu'est-ce qu'une fonction de générateur?

une fonction de générateur est effectivement une façon plus compacte et efficace d'écrire un Iterator . Il vous permet de définir une fonction (votre xrange ) qui va calculer et retourner valeurs tandis que vous êtes boucle sur elle :

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

cela créerait la sortie suivante:

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

vous pouvez également commander le $key dans le foreach en utilisant

yield $someKey => $someValue;

dans la fonction génératrice, $someKey est ce que vous voulez apparaissent pour $key et $someValue étant la valeur dans $val . Dans l'exemple de la question, c'est $i .

Quelle est la différence avec les fonctions normales?

Maintenant, vous pourriez vous demander pourquoi nous ne sommes pas simplement en utilisant natif de PHP range fonction pour atteindre ce résultat. Et vous avez raison. Le résultat serait le même. La différence, c'est comment on est arrivés là.

lorsque nous utilisons range PHP, va l'exécuter, créer le tableau entier des nombres en mémoire et return que tableau entier à la boucle foreach qui va ensuite passer et la sortie de la valeur. En d'autres termes, le foreach fonctionnera sur le tableau lui-même. La fonction range et la fonction foreach ne parlent qu'une seule fois. Pensez-y comme à l'obtention d'un colis par la poste. Le livreur vous remettra le paquet et partira. Et ensuite vous déballez le paquet entier, en enlevant ce qui est là-dedans.

lorsque nous utilisons la fonction generator, PHP entrera dans la fonction et l'exécutera jusqu'à ce qu'elle atteigne la fin ou un mot-clé yield . Quand il répond à un yield , il retournera alors quelle que soit la valeur à ce moment-là à la boucle extérieure. Puis il retourne dans la fonction de générateur et continue d'où il a cédé. Puisque votre xrange contient une boucle for , il s'exécutera et cédera jusqu'à ce que $max soit atteint. Pensez-y comme le foreach et le générateur qui joue au ping-pong.

pourquoi j'ai besoin de ça?

évidemment, les générateurs peuvent être utilisés pour travailler autour les limites de la mémoire. Selon votre environnement, Faire un range(1, 1000000) est fatal pour votre script, alors qu'un générateur fonctionne très bien. Ou comme Wikipedia le dit:

étant donné que les générateurs ne calculent les valeurs obtenues qu'à la demande, ils sont utiles pour représenter des séquences qui seraient coûteuses ou impossibles à calculer à la fois. Il s'agit par exemple de séquences infinies et de flux de données vivantes.

générateurs sont également censé être assez rapide. Mais gardez à l'esprit que lorsque nous parlons, nous sont généralement à discuter dans de très petits nombres. Donc avant de vous lancer et de changer tout votre code pour utiliser des générateurs, faites un benchmark pour voir où cela a du sens.

un autre cas D'utilisation pour les générateurs est les coroutines asynchrones. Le mot-clé yield ne renvoie pas seulement des valeurs, il les accepte également. Pour plus de détails, voir les deux excellents billets de blog ci-dessous.

Depuis quand puis-je utiliser yield ?

générateurs ont été introduits dans PHP 5.5 . Essayer d'utiliser yield avant cette version entraînera diverses erreurs d'analyse, en fonction du code qui suit le mot-clé. Donc si vous obtenez une erreur d'analyse à partir de ce code, mettez à jour votre PHP.

Sources et lectures complémentaires:

250
répondu Gordon 2017-09-14 14:42:33

yield Sert de mot-clé pour la définition de "générateurs" en PHP 5.5. Ok, alors qu'est ce qu'un générateur ?

de php.net:

Les générateurs

fournissent un moyen facile de mettre en œuvre des itérateurs simples sans frais généraux ou complexité de mise en œuvre d'une classe qui met en œuvre l'interface Itératrice.

Un générateur vous permet d'écrire du code qui utilise foreach pour parcourir un ensemble de données sans avoir besoin de construire un tableau en mémoire, ce qui peut vous amener à dépasser une limite de mémoire, ou nécessite une quantité considérable de temps de traitement pour générer. Au lieu de cela, vous pouvez écrire une fonction de générateur, qui est le même comme une fonction normale, sauf qu'au lieu de retourner une fois, un générateur peut produire autant de fois que nécessaire afin de fournir les valeurs pour être itéré.

de cet endroit: générateurs = générateurs, autres fonctions (juste un des fonctions simples) = fonctions.

donc, ils sont utiles quand:

  • vous devez faire des choses simples (ou des choses simples);

    générateur est vraiment beaucoup plus simple que la mise en œuvre de l'interface Iterator. d'un autre côté, les générateurs sont souvent moins fonctionnels. comparez-les .

  • vous avez besoin pour générer de grandes quantités de mémoire de sauvegarde de données;

    en fait pour sauver de la mémoire, nous pouvons juste générer des données nécessaires via des fonctions pour chaque itération de boucle, et après itération utiliser des déchets. donc voici les points principaux Est-code clair et probablement la performance. voir ce qui est mieux pour vos besoins.

  • vous devez générer une séquence, qui dépend des valeurs intermédiaires;

    c'est l'extension de la précédente pensée. les générateurs peuvent rendre les choses plus facile en comparaison avec les fonctions. cochez Fibonacci exemple , et essayez de faire séquence sans générateur. Aussi les générateurs peuvent fonctionner plus rapidement est ce cas, au moins en raison de stocker des valeurs intermédiaires dans les variables locales;

  • vous avez besoin pour améliorer les performances.

    ils peuvent travailler plus vite alors fonctions dans certains cas (voir prestation antérieure);

20
répondu QArea 2013-07-05 15:10:18

exemple simple

<?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#';
?>

sortie

#start main# {start[1,2,3,4,5,6,7,8,9,]end} #end main#
15
répondu Think Big 2016-09-28 12:29:06

cette fonction utilise le rendement:

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

est presque le même que celui sans:

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

avec une seule différence que a() retourne un générateur et b() juste un tableau simple. Vous pouvez itérer sur les deux.

en outre, le premier n'alloue pas un tableau complet et est donc moins exigeant en mémoire.

11
répondu tsusanka 2018-08-24 08:11:03

avec yield vous pouvez facilement décrire les points de rupture entre plusieurs tâches dans une seule fonction. C'est tout, il n'y a rien de spécial à ce sujet.

$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, ...);

si la tâche1 et la tâche2 sont fortement liées, mais vous avez besoin d'un point de rupture entre elles pour faire autre chose:

  • libérer de la mémoire entre le traitement de base de données les lignes
  • exécuter d'autres tâches qui fournissent la dépendance à la tâche suivante, mais qui sont non lié par la compréhension du code actuel
  • faire des appels asynchrones et d'attendre les résultats
  • et ainsi de suite ...

alors les générateurs sont la meilleure solution, parce que vous n'avez pas à diviser votre code en plusieurs fermetures ou le mélanger avec d'autres codes, ou utiliser des callbacks, etc... Vous utilisez juste yield pour ajouter un point de rupture, et vous pouvez continuer à partir de ce point de rupture si vous êtes prêt.

ajouter point de rupture sans générateur:

$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);
//...

ajouter le point de rupture avec les générateurs

$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);

note: il est facile de faire des erreurs avec les générateurs, donc toujours écrire des tests de l'unité avant de les mettre en œuvre! note2: utiliser des générateurs dans une boucle infinie est comme écrire une fermeture qui a une longueur infinie...

6
répondu inf3rno 2014-04-16 01:21:49