Menu
Coddy logo textTech

Patron Iterator

Fait partie de la section Object Oriented Programming du Journey Lua de Coddy — leçon 64 sur 70.

Lorsque vous avez une collection d'éléments stockés à l'intérieur d'un objet, vous voulez souvent les parcourir en boucle. La boucle for générique de Lua fonctionne à merveille avec des itérateurs personnalisés, et vous pouvez intégrer cette fonctionnalité directement dans vos classes.

Le patron Iterator permet à un objet de contrôler la manière dont ses données internes sont parcourues. Une approche élégante utilise la métaméthode __call, qui rend un objet appelable comme une fonction. Chaque appel retourne l'élément suivant jusqu'à ce qu'il n'y en ait plus.

local Collection = {}
Collection.__index = Collection

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

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

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

Avec __call défini, vous pouvez itérer en utilisant une boucle while ou passer l'objet directement à un for générique :

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

for item in bag do
    print(item)
end

La boucle appelle bag() de manière répétée. Lorsque __call retourne nil (plus d'éléments), la boucle s'arrête. Ce modèle encapsule la logique d'itération à l'intérieur de l'objet lui-même, vous donnant un contrôle total sur la manière — et l'ordre — dont les éléments sont accédés.

challenge icon

Défi

Facile

Construisons une classe Playlist que vous pouvez parcourir à l'aide de la boucle for générique de Lua ! Vous allez implémenter le modèle Iterator en utilisant la métaméthode __call, permettant à votre playlist d'être parcourue chanson par chanson.

Vous organiserez votre code sur deux fichiers :

  • Playlist.lua : Créez une classe Playlist qui stocke les chansons en interne. Votre classe a besoin d'un constructeur :new() qui initialise une liste de chansons vide et définit l'index d'itération à 0. Ajoutez une méthode :addSong(title) pour ajouter des chansons à la playlist. Plus important encore, implémentez la métaméthode __call de sorte que chaque fois que la playlist est « appelée », elle renvoie la chanson suivante de la liste (ou nil lorsqu'il n'y a plus de chansons).
  • main.lua : Importez votre module Playlist et créez une nouvelle playlist. Ajoutez-y des chansons en fonction des entrées que vous recevez, puis utilisez une boucle for générique pour parcourir la playlist et afficher chaque titre de chanson sur sa propre ligne.

La magie du modèle Iterator est que votre objet Playlist devient appelable — lorsqu'il est utilisé dans une boucle for, Lua l'appelle de manière répétée jusqu'à ce qu'il renvoie nil, vous donnant chaque chanson en séquence.

Vous recevrez trois entrées, chacune représentant un titre de chanson à ajouter à votre playlist (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

Aide-mémoire

Le pattern Iterator permet à un objet de contrôler la manière dont ses données internes sont parcourues en implémentant une logique d'itération personnalisée.

Utilisez la métaméthode __call pour rendre un objet appelable comme une fonction. Chaque appel renvoie l'élément suivant jusqu'à ce qu'il n'y en ait plus (renvoie nil) :

local Collection = {}
Collection.__index = Collection

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

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

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

Avec __call défini, vous pouvez utiliser l'objet directement dans une boucle for générique :

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

for item in bag do
    print(item)
end

La boucle appelle l'objet de manière répétée. Lorsque __call renvoie nil, la boucle s'arrête automatiquement.

Essayez vous-même

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

-- Lit trois titres de chansons à partir de l'entrée
local song1 = io.read()
local song2 = io.read()
local song3 = io.read()

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

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

-- TODO: Utiliser une boucle for générique pour parcourir la playlist
-- et afficher chaque titre de chanson sur sa propre ligne
-- Indice : for song in playlist 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 Object Oriented Programming