Menu
CoddyTech

Patron Iterator

Fait partie de la section Programmation orientée objet du Journey Lua de Coddy. Leçon 64 sur 70.

Lorsque tu as une collection d’éléments stockée dans un objet, tu veux souvent les parcourir en boucle. La boucle générique for de Lua fonctionne avec n’importe quelle fonction itératrice : elle appelle la fonction avant chaque passage et s’arrête lorsque la fonction renvoie nil.

Le modèle Iterator donne à l’objet une méthode qui renvoie une telle fonction. La fonction conserve sa position dans une variable locale ; cette position appartient donc à une boucle, et non à la collection :

local Collection = {}
Collection.__index = Collection

function Collection:new()
    local obj = { items = {} }
    setmetatable(obj, Collection)
    return obj
end

function Collection:add(item)
    table.insert(self.items, item)
end

function Collection:iter()
    local index = 0
    return function()
        index = index + 1
        return self.items[index]
    end
end

Chaque appel à bag:iter() crée une nouvelle fonction avec son propre index, en commençant à 0. La fonction est une fermeture : elle peut encore utiliser index et self après le retour de iter. Après le dernier élément, self.items[index] vaut nil, et la boucle s’arrête :

local bag = Collection:new()
bag:add("apple")
bag:add("banana")

for item in bag:iter() do
    print(item)  -- apple, puis banana
end

for item in bag:iter() do
    print(item)  -- apple, puis banana à nouveau
end

Conserve la position en dehors de la collection elle-même. Si l’objet stockait self.index et servait lui-même d’itérateur, la première boucle laisserait l’index à la fin ; une deuxième boucle n’afficherait donc rien, et une boucle interrompue prématurément avec break ferait commencer la boucle suivante au milieu.

challenge icon

Défi

Facile

Construisons une classe Playlist que tu peux parcourir avec la boucle générique for de Lua ! Tu vas implémenter le modèle Iterator à l’aide d’une méthode :iter() qui renvoie une fonction itératrice.

Tu vas répartir ton code dans deux fichiers :

  • Playlist.lua : crée une classe Playlist qui stocke les chansons en interne. Ta classe a besoin d’un constructeur :new() qui initialise une liste de chansons vide et d’une méthode :addSong(title) qui y ajoute une chanson. Plus important encore, ajoute une méthode :iter() qui renvoie une nouvelle fonction à chaque appel. Cette fonction conserve sa position dans une variable locale et renvoie la chanson suivante à chaque appel, ou nil lorsqu’il n’y a plus de chansons.
  • main.lua : charge ton module Playlist et crée une nouvelle liste de lecture. Ajoute-y des chansons à partir des entrées reçues, puis utilise une boucle générique for sur playlist:iter() pour afficher chaque titre de chanson sur sa propre ligne.

Comme chaque appel à :iter() commence à la première chanson, tu peux parcourir la même liste de lecture autant de fois que tu le souhaites.

Tu recevras trois entrées, chacune représentant un titre de chanson à ajouter à ta liste de lecture (dans l’ordre).

Par exemple, si les entrées sont Bohemian Rhapsody, Stairway to Heaven et Hotel California, la sortie devrait être :

Bohemian Rhapsody
Stairway to Heaven
Hotel California

Si les entrées sont Yesterday, Imagine et Let It Be, la sortie devrait être :

Yesterday
Imagine
Let It Be

Essayez vous-même

-- Charger le module Playlist
local Playlist = require('Playlist')

-- Lire trois titres de chansons depuis l’entrée
local song1 = io.read()
local song2 = io.read()
local song3 = io.read()

-- TODO: Créer une nouvelle playlist avec Playlist:new()

-- TODO: Ajouter les trois chansons à la playlist avec :addSong()

-- TODO: Utiliser une boucle for générique sur playlist:iter()
-- et afficher chaque titre de chanson sur sa propre ligne
-- Indice : for song in playlist:iter() do ... end
quiz iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Programmation orientée objet

Entraînez-vous par vous-même : Compilateur Lua en ligne