Patrón Iterador
Parte de la sección Object Oriented Programming del Journey de Lua de Coddy — lección 64 de 70.
Cuando tienes una colección de elementos almacenados dentro de un objeto, a menudo quieres recorrerlos. El bucle for genérico de Lua funciona de maravilla con iteradores personalizados, y puedes integrar esta funcionalidad directamente en tus clases.
El patrón Iterator permite que un objeto controle cómo se recorren sus datos internos. Un enfoque elegante utiliza el metamétodo __call, el cual hace que un objeto sea invocable como una función. Cada llamada devuelve el siguiente elemento hasta que no queden más.
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
Con __call definido, puedes iterar usando un bucle while o pasar el objeto directamente a un for genérico:
local bag = Collection:new()
bag:add("apple")
bag:add("banana")
for item in bag do
print(item)
end
El bucle llama a bag() repetidamente. Cuando __call devuelve nil (no hay más elementos), el bucle se detiene. Este patrón encapsula la lógica de iteración dentro del propio objeto, brindándote un control total sobre cómo —y en qué orden— se accede a los elementos.
Desafío
Fácil¡Vamos a construir una clase Playlist que puedas recorrer usando el bucle for genérico de Lua! Implementarás el patrón Iterator utilizando el metamétodo __call, permitiendo que tu lista de reproducción sea recorrida canción por canción.
Organizarás tu código en dos archivos:
Playlist.lua: Crea una clase Playlist que almacene canciones internamente. Tu clase necesita un constructor:new()que inicialice una lista de canciones vacía y establezca el índice de iteración en 0. Añade un método:addSong(title)para agregar canciones a la lista de reproducción. Lo más importante es implementar el metamétodo__callpara que cada vez que se "llame" a la lista de reproducción, devuelva la siguiente canción de la lista (onilcuando no haya más canciones).main.lua: Requiere tu módulo Playlist y crea una nueva lista de reproducción. Añade canciones basándote en las entradas que recibas, luego usa un bucleforgenérico para iterar a través de la lista de reproducción e imprimir el título de cada canción en su propia línea.
La magia del patrón Iterator es que tu objeto Playlist se vuelve ejecutable; cuando se usa en un bucle for, Lua lo llama repetidamente hasta que devuelve nil, entregándote cada canción en secuencia.
Recibirás tres entradas, cada una representando el título de una canción para 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 BeHoja de referencia
El patrón Iterator permite que un objeto controle cómo se recorren sus datos internos mediante la implementación de una lógica de iteración personalizada.
Utiliza el metamétodo __call para hacer que un objeto sea invocable como una función. Cada llamada devuelve el siguiente elemento hasta que no haya más (devuelve nil):
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
Con __call definido, puedes usar el objeto directamente en un bucle for genérico:
local bag = Collection:new()
bag:add("apple")
bag:add("banana")
for item in bag do
print(item)
end
El bucle llama al objeto repetidamente. Cuando __call devuelve nil, el bucle se detiene automáticamente.
Pruébalo tú mismo
-- Requerir el módulo Playlist
local Playlist = require('Playlist')
-- Leer tres títulos de canciones desde la entrada
local song1 = io.read()
local song2 = io.read()
local song3 = io.read()
-- TODO: Crear una nueva lista de reproducción usando Playlist:new()
-- TODO: Agregar las tres canciones a la lista de reproducción usando :addSong()
-- TODO: Usar un bucle for genérico para iterar a través de la lista de reproducción
-- y mostrar cada título de canción en su propia línea
-- Pista: for song in playlist do ... end
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Object Oriented Programming
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 formas