Patrón Iterador
Parte de la sección Programación orientada a objetos del Journey de Lua de Coddy. Lección 64 de 70.
Cuando tienes una colección de elementos almacenada dentro de un objeto, a menudo quieres recorrerla con un bucle. El bucle genérico for de Lua funciona con cualquier función iteradora: llama a la función antes de cada iteración y se detiene cuando la función devuelve nil.
El patrón Iterator le proporciona al objeto un método que devuelve una función de ese tipo. La función conserva su posición en una variable local, por lo que la posición pertenece a un bucle, no a la colección:
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
endCada llamada a bag:iter() crea una función nueva con su propio index, que comienza en 0. La función es un cierre: aún puede usar index y self después de que iter haya devuelto un valor. Después del último elemento, self.items[index] es nil y el bucle se detiene:
local bag = Collection:new()
bag:add("apple")
bag:add("banana")
for item in bag:iter() do
print(item) -- apple, luego banana
end
for item in bag:iter() do
print(item) -- apple, luego banana otra vez
endMantén la posición fuera de la colección. Si el objeto almacenara self.index y actuara como su propio iterador, el primer bucle dejaría el índice al final, así que un segundo bucle no imprimiría nada, y un bucle que se detuviera antes con break haría que el siguiente bucle comenzara a mitad de camino.
Desafío
Fácil¡Vamos a crear una clase Playlist que puedas recorrer con el bucle genérico for de Lua! Implementarás el patrón Iterator con un método :iter() que devuelve una función iteradora.
Organizarás el código en dos archivos:
Playlist.lua: Crea una clase Playlist que almacene las canciones internamente. Tu clase necesita un constructor:new()que inicialice una lista de canciones vacía y un método:addSong(title)que añada una canción a la lista. Y lo más importante, añade un método:iter()que devuelva una función nueva cada vez que se llame. Esa función conserva su posición en una variable local y devuelve la siguiente canción en cada llamada, onilcuando ya no queden canciones.main.lua: Carga tu módulo Playlist y crea una lista de reproducción. Añádele canciones según las entradas que recibas y, después, usa un bucle genéricoforsobreplaylist:iter()para imprimir el título de cada canción en su propia línea.
Como cada llamada a :iter() empieza por la primera canción, puedes recorrer la misma lista de reproducción cualquier cantidad de veces.
Recibirás tres entradas, cada una representa el título de una canción que se añadirá a tu lista de reproducción (en orden).
Por ejemplo, si las entradas son Bohemian Rhapsody, Stairway to Heaven y Hotel California, la salida debería ser:
Bohemian Rhapsody
Stairway to Heaven
Hotel CaliforniaSi las entradas son Yesterday, Imagine y Let It Be, la salida debería ser:
Yesterday
Imagine
Let It BePruébalo tú mismo
-- Importa el módulo Playlist
local Playlist = require('Playlist')
-- Lee tres títulos de canciones de la entrada
local song1 = io.read()
local song2 = io.read()
local song3 = io.read()
-- TODO: Crea una nueva lista de reproducción usando Playlist:new()
-- TODO: Añade las tres canciones a la lista de reproducción usando :addSong()
-- TODO: Usa un bucle for genérico sobre playlist:iter()
-- e imprime cada título de canción en su propia línea
-- Pista: for song in playlist:iter() do ... end
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación orientada a objetos
1El concepto de 'self'
Tablas con funciones'self' explícitoLa sintaxis de dos puntosPunto vs. dos puntosResumen - Punto en movimiento4Proyecto: Banco Digital
Configuración del proyectoMétodo de depósito2Patrón de prototipo de clase
El concepto de prototipoVinculación con __indexEl constructor :new()Inicialización de atributosInstancias independientesResumen - Fábrica de coches5Sobrecarga de operadores en POO
Sumar objetosRestar objetosConcatenar objetosComparar objetos (<, >)Resumen - Matemáticas de la billetera8Encapsulamiento
Convenciones de nomenclaturaClosures para privacidadAcceso mediante ClosuresTablas de solo lecturaLógica de validaciónResumen - Bóveda segura11Patrones de diseño (Lite)
Funciones FactoryTabla SingletonPatrón IteradorObserver (Listener)Resumen - Logger Factory3Estado y Comportamiento de los Objetos
Variables de InstanciaMétodos GetterMétodos SetterPropiedades CalculadasFormateo de StringsComprobaciones de IgualdadResumen - Calificación del Estudiante6Fundamentos de la herencia
Configuración de la herenciaHerencia de métodosExtender el constructorAñadir métodos hijosCompartido vs. ÚnicoResumen: Jerarquía de formasPractica por tu cuenta: Compilador de Lua online