Паттерн Iterator
Часть раздела Объектно-ориентированное программирование путешествия по Lua на Coddy. Урок 64 из 70.
Когда у тебя есть коллекция элементов, хранящаяся внутри объекта, часто возникает необходимость перебрать её элементы. Универсальный цикл for в Lua работает с любой функцией-итератором: он вызывает эту функцию перед каждой итерацией и останавливается, когда функция возвращает nil.
Паттерн «Итератор» предоставляет объекту метод, возвращающий такую функцию. Функция хранит свою позицию в локальной переменной, поэтому позиция относится к одному циклу, а не к коллекции:
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Каждый вызов bag:iter() создаёт новую функцию со своим index, начинающимся с 0. Эта функция является замыканием: она по-прежнему может использовать index и self после того, как iter вернула управление. После последнего элемента self.items[index] равно nil, и цикл останавливается:
local bag = Collection:new()
bag:add("apple")
bag:add("banana")
for item in bag:iter() do
print(item) -- apple, затем banana
end
for item in bag:iter() do
print(item) -- снова apple, затем banana
endХрани позицию отдельно от самой коллекции. Если бы объект хранил self.index и сам был итератором, после первого цикла индекс оказался бы в конце, поэтому второй цикл ничего бы не вывел, а если цикл досрочно остановился бы с помощью break, следующий цикл начался бы с середины.
Задание
ЛегкоДавай создадим класс Playlist, по которому можно проходить циклом с помощью обобщённого цикла for в Lua! Ты реализуешь паттерн «Итератор» с методом :iter(), который возвращает функцию-итератор.
Ты разместишь код в двух файлах:
Playlist.lua: создай класс Playlist, который хранит песни. В классе должны быть конструктор:new(), инициализирующий пустой список песен, и метод:addSong(title), который добавляет в него песню. Самое важное — добавь метод:iter(), который каждый раз при вызове возвращает новую функцию. Эта функция хранит свою позицию в локальной переменной и при каждом вызове возвращает следующую песню илиnil, если песен больше нет.main.lua: подключи модуль Playlist и создай новый плейлист. Добавь в него песни на основе полученных значений, затем используй обобщённый циклforпоplaylist:iter(), чтобы вывести название каждой песни на отдельной строке.
Поскольку каждый вызов :iter() начинает с первой песни, один и тот же плейлист можно перебирать любое количество раз.
Ты получишь три входных значения, каждое из которых представляет название песни для добавления в плейлист (в указанном порядке).
Например, если входные значения — Bohemian Rhapsody, Stairway to Heaven и Hotel California, вывод должен быть таким:
Bohemian Rhapsody
Stairway to Heaven
Hotel CaliforniaЕсли входные значения — Yesterday, Imagine и Let It Be, вывод должен быть таким:
Yesterday
Imagine
Let It BeПопробуйте сами
-- Подключите модуль Playlist
local Playlist = require('Playlist')
-- Считайте из входных данных названия трёх песен
local song1 = io.read()
local song2 = io.read()
local song3 = io.read()
-- TODO: Создайте новый плейлист с помощью Playlist:new()
-- TODO: Добавьте три песни в плейлист с помощью :addSong()
-- TODO: Используйте обобщённый цикл for для перебора playlist:iter()
-- и выведите название каждой песни на отдельной строке
-- Подсказка: for song in playlist:iter() do ... end
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Концепция 'self'
Таблицы с функциямиЯвный 'self'Синтаксис с двоеточиемТочка против двоеточияИтоги — Движущаяся точка4Проект: Цифровой банк
Настройка проектаМетод пополнения7Полиморфизм и переопределение
Переопределение методовВызов родительских методовУтиная типизацияОбщий интерфейсПроверка типаИтоги — Роли сотрудников2Паттерн прототипа класса
Концепция прототипаСвязывание через __indexКонструктор :new()Инициализация атрибутовНезависимые экземплярыИтоги — Автозавод5Перегрузка операторов в ООП
Сложение объектовВычитание объектовКонкатенация объектовСравнение объектов (<, >)Повторение — Математика кошелька8Инкапсуляция
Соглашения об именованииЗамыкания для приватностиДоступ через замыканияТаблицы только для чтенияЛогика валидацииИтоги — Защищенное хранилище11Паттерны проектирования (Lite)
Фабричные функцииТаблица SingletonПаттерн IteratorObserver (Слушатель)Итоги — Logger Factory3Состояние и поведение объектов
Переменные экземпляраГеттерыСеттерыВычисляемые свойстваФорматирование строкПроверка на равенствоПовторение — Оценка студента6Основы наследования
Настройка наследованияНаследование методовРасширение конструктораДобавление дочерних методовОбщее и уникальноеИтоги: Иерархия фигурПотренируйтесь самостоятельно: Онлайн-компилятор Lua