Menu
CoddyTech

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
end

Cada 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
end

Manté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.

challenge icon

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, o nil cuando 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érico for sobre playlist: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 California

Si las entradas son Yesterday, Imagine y Let It Be, la salida debería ser:

Yesterday
Imagine
Let It Be

Prué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
quiz iconPonte a prueba

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

Practica por tu cuenta: Compilador de Lua online