Qu'est-ce que c'est yield
?
Le yield
mot-clé renvoie les données d'une fonction de générateur:
Le cœur d'une fonction de générateur est le mot-clé yield. 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 revenir, yield fournit à la place une valeur au code en boucle sur le générateur et suspend l'exécution de la fonction du générateur.
Qu'est-ce qu'une fonction générateur?
Une fonction de générateur est en fait un moyen plus compact et plus efficace d'écrire un itérateur . Il vous permet de définir une fonction (votre xrange
) qui calculera et renverra des valeurs pendant que vous la parcourez :
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 contrôler le $key
dans le foreach
en utilisant
yield $someKey => $someValue;
Dans la fonction de générateur, $someKey
est ce que vous voulez voir apparaître $key
et $someValue
être la valeur $val
. Dans l'exemple de la question, c'est $i
.
Quelle est la différence avec les fonctions normales?
Maintenant, vous vous demandez peut-être pourquoi nous n'utilisons pas simplement la range
fonction native de PHP pour obtenir cette sortie. Et vous avez raison. La sortie serait la même. La différence, c'est comment nous y sommes arrivés.
Lorsque nous utilisons range
PHP, l'exécutons, créent le tableau entier de nombres en mémoire et return
ce tableau entier dans la foreach
boucle qui va ensuite le parcourir et sortir les valeurs. En d'autres termes, le foreach
va fonctionner sur la baie de disques elle-même. La range
fonction et le foreach
seul "parler" une fois. Pensez-y comme recevoir un colis par la poste. Le livreur vous remettra le colis et partira. Et puis vous déballez l'ensemble complet, en retirant tout ce qui s'y trouve.
Lorsque nous utilisons la fonction de générateur, PHP entrera dans la fonction et l'exécutera jusqu'à ce qu'elle rencontre la fin ou un yield
mot clé. Quand il rencontre un yield
, il retournera alors quelle que soit la valeur à ce moment à la boucle externe. Ensuite, il revient dans la fonction de générateur et continue d'où il a cédé. Puisque votre xrange
détient une for
boucle, il s'exécutera et cédera jusqu'à ce qu'il $max
soit atteint. Pensez-y comme le foreach
et le générateur jouant au ping-pong.
Pourquoi ai-je besoin de ça?
De toute évidence, les générateurs peuvent être utilisés pour contourner les limites de mémoire. En fonction de votre environnement, faire un range(1, 1000000)
sera fatal à votre script alors que la même chose avec un générateur fonctionnera très bien. Ou, comme le dit Wikipedia:
Étant donné que les générateurs calculent leurs valeurs produites uniquement sur 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 en direct.
Les générateurs sont également censés être assez rapides. Mais gardez à l'esprit que lorsque nous parlons de rapidité, nous parlons généralement en très petit nombre. Donc, avant de vous enfuir et de modifier tout votre code pour utiliser des générateurs, faites un test pour voir où cela a du sens.
Un autre cas d'utilisation pour les générateurs est les coroutines asynchrones. Le yield
mot-clé ne renvoie pas seulement des valeurs mais il les accepte également. Pour plus de détails à ce sujet, consultez les deux excellents articles de blog liés ci-dessous.
Depuis quand puis-je utiliser yield
?
Des générateurs ont été introduits en PHP 5.5 . Essayer d'utiliser yield
avant cette version entraînera diverses erreurs d'analyse, selon le code qui suit le mot clé. Donc, si vous obtenez une erreur d'analyse de ce code, mettez à jour votre PHP.
Sources et lectures complémentaires:
yeild
, disons, une solution comme celle-ci: ideone.com/xgqevMreturn range(1,100000000)
etfor ($i=0; $i<100000000; $i++) yield $i
Cette fonction utilise le rendement:
est presque le même que celui-ci sans:
La seule différence est qu'il
a()
renvoie un générateur etb()
juste un simple tableau. Vous pouvez parcourir les deux.De plus, le premier n'alloue pas un tableau complet et est donc moins gourmand en mémoire.
la source
exemple simple
production
exemple avancé
production
la source
yield
Le mot-clé sert à la définition des "générateurs" en PHP 5.5. Ok, alors qu'est-ce qu'un générateur ?De php.net:
De cet endroit: générateurs = générateurs, autres fonctions (juste une simple fonction) = fonctions.
Ils sont donc utiles lorsque:
vous devez faire des choses simples (ou des choses simples);
générateur est vraiment beaucoup plus simple que d'implémenter l'interface Iterator. d'autre part, il est évident que les générateurs sont moins fonctionnels. les comparer .
vous devez générer de GRANDES quantités de données pour économiser de la mémoire;
en fait, pour économiser de la mémoire, nous pouvons simplement générer les données nécessaires via des fonctions pour chaque itération de boucle, et après l'itération, utiliser les ordures. voici donc les points principaux: un code clair et probablement des performances. voyez ce qui est mieux pour vos besoins.
vous devez générer une séquence qui dépend de valeurs intermédiaires;
cela prolonge la pensée précédente. les générateurs peuvent faciliter les choses par rapport aux fonctions. vérifiez l' exemple de Fibonacci , et essayez de faire une séquence sans générateur. Les générateurs peuvent également fonctionner plus rapidement dans ce cas, du moins en raison du stockage des valeurs intermédiaires dans des variables locales;
vous devez améliorer les performances.
ils peuvent fonctionner plus rapidement que les fonctions dans certains cas (voir avantage précédent);
la source
Avec,
yield
vous pouvez facilement décrire les points d'arrêt entre plusieurs tâches dans une seule fonction. C'est tout, il n'y a rien de spécial à ce sujet.Si task1 et task2 sont fortement liés, mais vous avez besoin d'un point d'arrêt entre eux pour faire autre chose:
alors les générateurs sont la meilleure solution, car vous n'avez pas à diviser votre code en plusieurs fermetures ou à le mélanger avec un autre code, ou à utiliser des rappels, etc ... Vous utilisez simplement
yield
pour ajouter un point d'arrêt, et vous pouvez continuer à partir de là point d'arrêt si vous êtes prêt.Ajouter un point d'arrêt sans générateurs:
Ajouter un point d'arrêt avec des générateurs
note: Il est facile de se tromper avec les générateurs, alors écrivez toujours des tests unitaires avant de les implémenter! note2: Utiliser des générateurs dans une boucle infinie, c'est comme écrire une fermeture de longueur infinie ...
la source
Aucune des réponses ci-dessus ne montre un exemple concret utilisant des tableaux massifs peuplés de membres non numériques. Voici un exemple utilisant un tableau généré par
explode()
sur un gros fichier .txt (262 Mo dans mon cas d'utilisation):Le résultat était:
Comparez maintenant cela à un script similaire, en utilisant le
yield
mot - clé:La sortie de ce script était:
De toute évidence, les économies d’utilisation de la mémoire ont été considérables (ΔMemoryUsage -----> ~ 270,5 Mo dans le premier exemple, ~ 450B dans le deuxième exemple).
la source
Un aspect intéressant, qui mérite d'être discuté ici, cède par référence . Chaque fois que nous devons changer un paramètre de sorte qu'il se reflète à l'extérieur de la fonction, nous devons passer ce paramètre par référence. Pour appliquer cela aux générateurs, nous ajoutons simplement une esperluette
&
au nom du générateur et à la variable utilisée dans l'itération:L'exemple ci-dessus montre comment la modification des valeurs itérées dans la
foreach
boucle modifie la$from
variable dans le générateur. Cela est dû au fait qu'il$from
est fourni par référence en raison de l'esperluette avant le nom du générateur. Pour cette raison, la$value
variable dans laforeach
boucle est une référence à la$from
variable dans la fonction de générateur.la source
Le code ci-dessous illustre comment l'utilisation d'un générateur renvoie un résultat avant la fin, contrairement à l'approche non génératrice traditionnelle qui renvoie un tableau complet après une itération complète. Avec le générateur ci-dessous, les valeurs sont retournées lorsqu'elles sont prêtes, pas besoin d'attendre qu'un tableau soit complètement rempli:
la source