Vous pouvez voir les itérateurs comme un exemple très concret du modèle producteur --> consommateur.
Un itérateur produit des articles qui seront ensuite consommés par le corps de la boucle.
Il semble donc approprié d'utiliser les coroutines pour écrire les itérateurs.
En fait, les coroutines fournissent un outil puissant pour cette tâche.
Car encore une fois, la principale caractéristique des coroutines est la capacité à tourner à l'envers, la relation entre l'appelant et l'appelé.
Avec cette fonctionnalité, vous pouvez donc écrire un itérateur sans vous soucier de conserver l'état entre les appels successifs à l'itérateur.
Pour illustrer ce type d'utilisation, vous allez écrire un itérateur afin de parcourir toutes les permutations d'un tableau donné.
Ce n'est pas une tâche facile et il est plus simple d'écrire une fonction récursive qui génère toutes les permutations.
L'idée est simple: Mettez chaque élément du tableau en dernière position et générez de façon récursive toutes les permutations possibles des éléments restants.
Ceci étant, le code pourrait ressembler à ce qui suit:
function permgen(a, n) if n == 0 then printResult(a) else for i = 1,n do -- on met le ième élément à la dernière place. a[n], a[i] = a[i], a[n] -- on génère les permutations des autres éléments. permgen(a, n - 1) -- on restaure le ième élément. a[n], a[i] = a[i], a[n] end end end
Il faut maintenant définir une fonction printResult appropriée et appeler permgen avec les arguments adéquats.
function printResult(a) for i,v in ipairs(a) do io.write(v, " ") end io.write("\n") end permgen ({1,2,3,4}, 4)
Changez maintenant printResult par une coroutine.yield.
function permgen(a, n) if n == 0 then coroutine.yield(a) else ...
Maintenant définissez une fonction qui fasse que le générateur tourne dans une coroutine, puis crée la fonction d'itération.
L'itérateur reprendra simplement la coroutine pour produire la permutation suivante.
function Perm (a) local n = table.getn(a) local co = coroutine.create(function() permgen(a, n) end) return function() -- iterator local code, res = coroutine.resume(co) return res end end
La fonction Perm utilise un modèle commun en Lua, qui enveloppe un appel permettant de reprendre ses coroutines correspondantes à l'intérieur d'une fonction.
Ce modèle est si courant que Lua fournit une fonction spéciale: coroutine.wrap().
Comme create , wrap crée une nouvelle coroutine.
Contrairement à create , wrap ne retourne pas la coroutine mais, une fonction qui, lorsqu'elle est appelée, reprend la coroutine.
Contrairement à resume , cette fonction ne retourne pas de code d'erreur, mais soulève l'erreur au cas ou.
function Perm(a) local n = table.getn(a) return coroutine.wrap(function() permgen(a, n) end) end
Habituellement, coroutine.wrap() est plus simple à utiliser que coroutine.create() car elle donne exactement ce dont vous avez besoin: une fonction de reprise.
Mais elle est aussi moins souple, car il n'existe aucun moyen de vérifier son statut et les erreurs éventuelles.