Menu
CoddyTech

Wzorzec iteratora

Część sekcji Programowanie obiektowe ścieżki Lua w Coddy. Lekcja 64 z 70.

Gdy masz kolekcję elementów przechowywanych w obiekcie, często chcesz je przeglądać. Ogólna pętla for w Lua działa z dowolną funkcją iteratora: wywołuje tę funkcję przed każdym przebiegiem i zatrzymuje się, gdy funkcja zwróci nil.

Wzorzec Iterator daje obiektowi metodę, która zwraca taką funkcję. Funkcja przechowuje swoją pozycję w zmiennej lokalnej, więc pozycja należy do jednej pętli, a nie do kolekcji:

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

Każde wywołanie bag:iter() tworzy nową funkcję z własnym index, którego wartość początkowa to 0. Funkcja jest domknięciem: nadal może używać index i self po zwróceniu przez iter. Po ostatnim elemencie self.items[index] ma wartość nil i pętla się zatrzymuje:

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

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

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

Nie przechowuj pozycji w samej kolekcji. Gdyby obiekt przechowywał self.index i był własnym iteratorem, po pierwszej pętli indeks pozostałby na końcu, więc druga pętla niczego by nie wypisała, a pętla przerwana wcześniej za pomocą break sprawiłaby, że następna pętla zaczęłaby w połowie.

challenge icon

Wyzwanie

Łatwy

Zbudujmy klasę Playlist, po której można iterować za pomocą ogólnej pętli for w Lua! Zaimplementujesz wzorzec Iterator za pomocą metody :iter(), która zwraca funkcję iteratora.

Rozmieścisz swój kod w dwóch plikach:

  • Playlist.lua: Utwórz klasę Playlist, która przechowuje utwory. Klasa powinna mieć konstruktor :new(), który inicjalizuje pustą listę utworów, oraz metodę :addSong(title), która dodaje do niej utwór. Co najważniejsze, dodaj metodę :iter(), która za każdym razem, gdy zostanie wywołana, zwraca nową funkcję. Ta funkcja przechowuje swoją pozycję w zmiennej lokalnej i przy każdym wywołaniu zwraca następny utwór albo nil, gdy nie ma już więcej utworów.
  • main.lua: Załaduj moduł Playlist i utwórz nową playlistę. Dodaj do niej utwory na podstawie otrzymanych danych wejściowych, a następnie użyj ogólnej pętli for po playlist:iter(), aby wypisać tytuł każdego utworu w osobnym wierszu.

Ponieważ każde wywołanie :iter() rozpoczyna od pierwszego utworu, po tej samej playliście można iterować dowolną liczbę razy.

Otrzymasz trzy dane wejściowe, z których każda reprezentuje tytuł utworu do dodania do playlisty (w podanej kolejności).

Na przykład, jeśli dane wejściowe to Bohemian Rhapsody, Stairway to Heaven i Hotel California, dane wyjściowe powinny wyglądać tak:

Bohemian Rhapsody
Stairway to Heaven
Hotel California

Jeśli dane wejściowe to Yesterday, Imagine i Let It Be, dane wyjściowe powinny wyglądać tak:

Yesterday
Imagine
Let It Be

Spróbuj swoich sił

-- Załaduj moduł Playlist
local Playlist = require('Playlist')

-- Wczytaj z wejścia trzy tytuły piosenek
local song1 = io.read()
local song2 = io.read()
local song3 = io.read()

-- TODO: Utwórz nową playlistę za pomocą Playlist:new()

-- TODO: Dodaj trzy piosenki do playlisty za pomocą :addSong()

-- TODO: Użyj ogólnej pętli for do iteracji po playlist:iter()
-- i wypisz każdy tytuł piosenki w osobnym wierszu
-- Wskazówka: for song in playlist:iter() do ... end
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator Lua online