Паттерн Iterator
Часть раздела Объектно-ориентированное программирование путешествия по Lua на Coddy. Урок 64 из 70.
Когда у вас есть коллекция элементов, хранящихся внутри объекта, вы часто хотите перебрать их. Универсальный цикл for в Lua отлично работает с пользовательскими итераторами, и вы можете встроить эту функциональность непосредственно в свои классы.
Паттерн Iterator позволяет объекту управлять тем, как перебираются его внутренние данные. Один из элегантных подходов использует метаметод __call, благодаря которому объект можно вызывать как функцию. Каждый вызов возвращает следующий элемент, пока больше не останется.
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
Определив __call, вы можете выполнять итерацию с помощью цикла while или напрямую передать объект в generic for:
local bag = Collection:new()
bag:add("apple")
bag:add("banana")
for item in bag do
print(item)
end
Цикл многократно вызывает bag(). Когда __call возвращает nil (больше нет элементов), цикл останавливается. Этот шаблон инкапсулирует логику итерации внутри самого объекта, предоставляя вам полный контроль над тем, как и в каком порядке осуществляется доступ к элементам.
Задание
ЛегкоДавайте создадим класс Playlist, через который можно выполнять итерацию с помощью generic for loop Lua! Вы реализуете паттерн Iterator, используя метаметод __call, благодаря чему можно будет проходить по Playlist песня за песней.
Вы организуете код в двух файлах:
Playlist.lua: Создайте класс Playlist, который хранит песни внутри себя. Вашему классу нужен конструктор:new(), инициализирующий пустой список песен и устанавливающий индекс iteration в 0. Добавьте метод:addSong(title)для добавления песен в Playlist. Самое главное — реализуйте метаметод__call, чтобы каждый раз, когда Playlist «called», он возвращал следующую песню из списка (илиnil, когда больше нет песен).main.lua: Подключите свой module Playlist и создайте новый Playlist. Добавьте в него песни на основе полученных входных данных, затем используйте genericforloop для прохода по Playlist и вывода названия каждой песни на отдельной строке.
Магия паттерна Iterator заключается в том, что ваш объект Playlist становится вызываемым: при использовании в for loop Lua repeatedly вызывает его, пока он не вернёт nil, предоставляя вам каждую песню по порядку.
Вы получите три входных значения, каждое из которых представляет название песни, которую нужно добавить в Playlist (в указанном порядке).
Например, если входные данные — 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 для перебора плейлиста
-- и вывести название каждой песни на отдельной строке
-- Подсказка: for song in playlist do ... end
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Концепция 'self'
Таблицы с функциямиЯвный 'self'Синтаксис с двоеточиемТочка против двоеточияИтоги — Движущаяся точка4Проект: Цифровой банк
Настройка проектаМетод пополнения7Полиморфизм и переопределение
Переопределение методовВызов родительских методовУтиная типизацияОбщий интерфейсПроверка типаИтоги — Роли сотрудников2Паттерн прототипа класса
Концепция прототипаСвязывание через __indexКонструктор :new()Инициализация атрибутовНезависимые экземплярыИтоги — Автозавод5Перегрузка операторов в ООП
Сложение объектовВычитание объектовКонкатенация объектовСравнение объектов (<, >)Повторение — Математика кошелька8Инкапсуляция
Соглашения об именованииЗамыкания для приватностиДоступ через замыканияТаблицы только для чтенияЛогика валидацииИтоги — Защищенное хранилище11Паттерны проектирования (Lite)
Фабричные функцииТаблица SingletonПаттерн IteratorObserver (Слушатель)Итоги — Logger Factory3Состояние и поведение объектов
Переменные экземпляраГеттерыСеттерыВычисляемые свойстваФорматирование строкПроверка на равенствоПовторение — Оценка студента6Основы наследования
Настройка наследованияНаследование методовРасширение конструктораДобавление дочерних методовОбщее и уникальноеИтоги: Иерархия фигурПотренируйтесь самостоятельно: Онлайн-компилятор Lua