43 votes

Quelle est la différence entre un générateur et un réseau ?

Aujourd'hui, l'équipe PHP a publié la PHP 5.5.0 qui comprend la prise en charge de générateurs . Lecture la documentation j'ai remarqué qu'il fait exactement ce qu'il pourrait faire avec un tableau.

L'équipe PHP générateur exemple :

// Only PHP 5.5
function gen_one_to_three() {
    for ($i = 1; $i <= 3; $i++) {
        // Note that $i is preserved between yields.
        yield $i;
    }
}

$generator = gen_one_to_three();
foreach ($generator as $value) {
    echo "$value\n";
}

Résultat :

1
2
3

Mais je peux faire la même chose en utilisant des tableaux. Et je peux toujours rester compatible avec les versions antérieures de PHP.

Jetez un coup d'œil :

// Compatible with 4.4.9!
function gen_one_to_three() {
    $results = array();
    for ($i = 1; $i <= 3; $i++) {
        $results[] = $i;
    }

    return $results;
}

$generator = gen_one_to_three();
foreach ($generator as $value) {
    echo "$value\n";
}

La question est donc Le problème est le suivant : quel est le but de l'existence de cette nouvelle fonctionnalité ? J'ai pu jouer tous les exemples de la documentation sans utiliser la nouvelle fonctionnalité, en la remplaçant par un tableau.

Quelqu'un peut-il donner une bonne explication et peut-être un exemple qui n'est pas nécessairement impossible avec les anciennes versions, mais dont l'utilisation de générateurs peut aider au développement ?

66voto

ircmaxell Points 74865

La différence se situe au niveau de l'efficacité. Par exemple, de nombreux langages autres que PHP comprennent deux fonctions range fonctions, range() y xrange() . C'est un très bon exemple de générateurs et de leur utilité. Construisons le nôtre :

function range($start, $end) {
    $array = array();
    for ($i = $start; $i <= $end; $i++) {
        $array[] = $i;
    }
    return $array;
}

C'est très simple. Cependant, pour les grandes plages, cela prend une quantité ÉNORME de mémoire. Si nous essayons de l'exécuter avec $start = 0 y $end = 100000000 nous risquons de manquer de mémoire !

Mais si on utilisait un générateur :

function xrange($start, $end) {
    for ($i = $start; $i <= $end; $i++) {
        yield $i;
    }
}

Nous utilisons maintenant une mémoire constante, tout en ayant un "tableau" (comme une structure) sur lequel nous pouvons itérer (et utiliser d'autres itérateurs) dans le même espace.

Ce n'est pas le cas. remplacer un tableau, mais il fournit un moyen efficace d'éviter d'avoir besoin de la mémoire...

Mais elle permet également de réaliser des économies en termes de génération d'articles. Comme chaque résultat est généré au fur et à mesure des besoins, vous pouvez retarder l'exécution (récupération ou calcul) de chaque élément jusqu'à ce que vous en ayez besoin. Ainsi, par exemple, si vous devez extraire un élément d'une base de données et effectuer un traitement complexe autour de chaque ligne, vous pouvez retarder cette opération avec un générateur jusqu'à ce que vous ayez réellement besoin de cette ligne :

function fetchFromDb($result) {
    while ($row = $result->fetchArray()) {
        $record = doSomeComplexProcessing($row);
        yield $record;
    }
}

Ainsi, si vous n'avez besoin que des trois premiers résultats, vous ne traiterez que les trois premiers enregistrements.

Pour plus d'informations, j'ai écrit un article de blog sur ce sujet précis.

14voto

TimWolla Points 11210

Les générateurs permettent évaluation paresseuse de déclarations complexes. De cette façon, vous économisez de la mémoire car vous n'avez pas à tout allouer en même temps.

En plus d'être tous deux itérables, ils ne sont pas du tout les mêmes. Un site array est une structure de données, un générateur ne l'est pas.

12voto

Mark Baker Points 90240

Un tableau doit contenir chaque valeur sur laquelle on boucle avant de commencer la boucle ; un générateur crée chaque valeur "à la volée" au fur et à mesure qu'elle est demandée, donc beaucoup moins de mémoire ;

Un tableau fonctionne avec les valeurs qu'il contient, et doit être pré-rempli avec ces valeurs ; un générateur peut créer des valeurs selon des critères spéciaux à utiliser directement... par exemple une séquence fibonnacienne, ou des lettres d'un alphabet non-A-Z (calculé par la valeur numérique UTF-8) permettant effectivement alphaRange('','') ;

EDIT

function fibonacci($count) {
    $prev = 0;
    $current = 1;

    for ($i = 0; $i < $count; ++$i) {
        yield $prev;
        $next = $prev + $current;
        $prev = $current;
        $current = $next;
    }
}

foreach (fibonacci(48) as $i => $value) {
    echo $i , ' -> ' , $value, PHP_EOL;
}

EDIT

Juste pour le plaisir, voici un générateur qui retournera l'alphabet hébreu sous forme de caractères UTF-8

function hebrewAlphabet() {
    $utf8firstCharacter = 1488;
    $utf8lastCharacter = 1514;
    for ($character = $utf8firstCharacter; $character <= $utf8lastCharacter; ++$character) {
        yield html_entity_decode('&#'.$character.';', ENT_NOQUOTES, 'UTF-8');
    };
}

foreach(hebrewAlphabet() as $character) {
    echo $character, ' ';
}

4voto

Luigi Siri Points 1411

Comme dans Python :

Lorsqu'une itération sur un ensemble d'éléments commence en utilisant l'instruction for, le générateur est exécuté. Lorsque le code de la fonction du générateur atteint une instruction "yield", le générateur cède son exécution à la boucle for, renvoyant une nouvelle valeur de l'ensemble. La fonction du générateur peut générer autant de valeurs (éventuellement infinies) qu'elle le souhaite, en les retournant chacune à son tour.

...Generators exécute les instructions yield une par une, en marquant une pause entre elles pour renvoyer l'exécution à la boucle for principale.

- learnpython.org

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X