Iterator-Muster
Teil des Abschnitts Objektorientierte Programmierung der Lua-Journey von Coddy. Lektion 64 von 70.
Wenn du eine Sammlung von Elementen in einem Objekt gespeichert hast, möchtest du oft alle Elemente durchlaufen. Die generische for-Schleife von Lua funktioniert mit jeder Iteratorfunktion: Sie ruft die Funktion vor jedem Durchlauf auf und hält an, wenn die Funktion nil zurückgibt.
Das Iterator-Muster gibt dem Objekt eine Methode, die eine solche Funktion zurückgibt. Die Funktion behält ihre Position in einer lokalen Variablen, sodass die Position zu einer Schleife und nicht zur Sammlung gehört:
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
endJeder Aufruf von bag:iter() erstellt eine neue Funktion mit einem eigenen index, der bei 0 beginnt. Die Funktion ist eine Closure: Sie kann index und self weiterhin verwenden, nachdem iter zurückgegeben wurde. Nach dem letzten Element ist self.items[index] nil, und die Schleife wird beendet:
local bag = Collection:new()
bag:add("apple")
bag:add("banana")
for item in bag:iter() do
print(item) -- apple, dann banana
end
for item in bag:iter() do
print(item) -- apple, dann wieder banana
endHalte die Position aus der Sammlung selbst heraus. Wenn das Objekt self.index speicherte und als eigener Iterator fungierte, würde die erste Schleife den Index am Ende zurücklassen, sodass eine zweite Schleife nichts ausgeben würde, und eine Schleife, die vorzeitig mit break beendet wird, würde dazu führen, dass die nächste Schleife in der Mitte beginnt.
Aufgabe
EinfachLass uns eine Playlist-Klasse erstellen, die du mit Lua's generischer for-Schleife durchlaufen kannst! Du implementierst das Iterator-Muster mit einer :iter()-Methode, die eine Iteratorfunktion zurückgibt.
Du verteilst deinen Code auf zwei Dateien:
Playlist.lua: Erstelle eine Playlist-Klasse, die Songs intern speichert. Deine Klasse benötigt einen Konstruktor:new(), der eine leere Songliste initialisiert, und eine Methode:addSong(title), die einen Song hinzufügt. Am wichtigsten ist, dass du eine Methode:iter()hinzufügst, die jedes Mal, wenn sie aufgerufen wird, eine neue Funktion zurückgibt. Diese Funktion speichert ihre Position in einer lokalen Variable und gibt bei jedem Aufruf den nächsten Song zurück odernil, wenn keine Songs mehr vorhanden sind.main.lua: Binde dein Playlist-Modul ein und erstelle eine neue Playlist. Füge ihr basierend auf den Eingaben Songs hinzu und verwende dann eine generischefor-Schleife überplaylist:iter(), um jeden Songtitel in einer eigenen Zeile auszugeben.
Da jeder Aufruf von :iter() beim ersten Song beginnt, kann dieselbe Playlist beliebig oft durchlaufen werden.
Du erhältst drei Eingaben, die jeweils einen Songtitel darstellen, der deiner Playlist hinzugefügt werden soll (in dieser Reihenfolge).
Wenn die Eingaben beispielsweise Bohemian Rhapsody, Stairway to Heaven und Hotel California lauten, sollte die Ausgabe folgendermaßen aussehen:
Bohemian Rhapsody
Stairway to Heaven
Hotel CaliforniaWenn die Eingaben Yesterday, Imagine und Let It Be lauten, sollte die Ausgabe folgendermaßen aussehen:
Yesterday
Imagine
Let It BeProbier es selbst
-- Binde das Modul Playlist mit require ein
local Playlist = require('Playlist')
-- Lies drei Songtitel aus der Eingabe ein
local song1 = io.read()
local song2 = io.read()
local song3 = io.read()
-- TODO: Erstelle mit Playlist:new() eine neue Playlist
-- TODO: Füge die drei Songs mit :addSong() zur Playlist hinzu
-- TODO: Verwende eine generische for-Schleife über playlist:iter()
-- und gib jeden Songtitel in einer eigenen Zeile aus
-- Hinweis: for song in playlist:iter() do ... end
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Das 'self'-Konzept
Tabellen mit FunktionenExplizites 'self'Die Doppelpunkt-SyntaxPunkt vs. DoppelpunktZusammenfassung – Beweglicher Punkt4Projekt: Digitale Bank
Projekt-SetupEinzahlungsmethode7Polymorphie & Überschreiben
Methoden überschreibenElternmethoden aufrufenDuck TypingGemeinsames InterfaceTyp prüfenRückblick - Mitarbeiterrollen10Projekt: Formen-Manager
Projekt-SetupRechteck-KlasseKreis-KlasseUmfang-MethodeFormen-SammlungGesamtflächeFormen filtern2Klassen-Prototyp-Muster
Das Prototyp-KonzeptVerknüpfung mit __indexDer :new()-KonstruktorAttribute initialisierenUnabhängige InstanzenZusammenfassung – Autofabrik5Operatorüberladung in OOP
Objekte addierenObjekte subtrahierenObjekte verkettenObjekte vergleichen (<, >)Rückblick – Wallet-Mathematik8Kapselung
NamenskonventionenClosures für private DatenZugriff über ClosuresSchreibgeschützte TabellenValidierungslogikZusammenfassung – Sicherer Tresor11Entwurfsmuster (Lite)
Factory-FunktionenSingleton-TabelleIterator-MusterObserver (Listener)Wiederholung – Logger-Factory3Objektzustand und -verhalten
InstanzvariablenGetter-MethodenSetter-MethodenBerechnete EigenschaftenStrings formatierenGleichheitsprüfungenZusammenfassung – Studentennote6Grundlagen der Vererbung
Das Vererbungs-SetupMethoden vererbenDen Konstruktor erweiternChild-Methoden hinzufügenGemeinsam vs. EinzigartigZusammenfassung - Formen-Hierarchie9Komposition & Mixins
Has-A-BeziehungDelegationEinfache MixinsAnwendung mehrerer MixinsMixins vs. VererbungZusammenfassung – Roboter-Montage12Abschluss-Herausforderungen
Wiederholung - InventarsystemWiederholung - BenutzerverwaltungWiederholung - Score-TrackerFinaler OOP-CheckÜbe selbstständig: Online-Lua-Compiler