Menu
Coddy logo textTech

Padrão Iterator

Parte da seção Object Oriented Programming do Journey de Lua da Coddy — lição 64 de 70.

Quando você tem uma coleção de itens armazenados dentro de um objeto, muitas vezes você deseja percorrê-los. O loop for genérico do Lua funciona perfeitamente com iteradores personalizados, e você pode construir essa funcionalidade diretamente em suas classes.

O padrão Iterator permite que um objeto controle como seus dados internos são percorridos. Uma abordagem elegante utiliza o metamétodo __call, que torna um objeto chamável como uma função. Cada chamada retorna o próximo item até que não haja mais nenhum.

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

Com __call definido, você pode iterar usando um loop while ou passar o objeto diretamente para um for genérico:

local bag = Collection:new()
bag:add("apple")
bag:add("banana")

for item in bag do
    print(item)
end

O loop chama bag() repetidamente. Quando __call retorna nil (não há mais itens), o loop para. Este padrão encapsula a lógica de iteração dentro do próprio objeto, oferecendo controle total sobre como — e em que ordem — os itens são acessados.

challenge icon

Desafio

Fácil

Vamos construir uma classe Playlist que você pode percorrer usando o loop for genérico do Lua! Você implementará o padrão Iterator usando o metamétodo __call, permitindo que sua playlist seja percorrida música por música.

Você organizará seu código em dois arquivos:

  • Playlist.lua: Crie uma classe Playlist que armazena músicas internamente. Sua classe precisa de um construtor :new() que inicializa uma lista de músicas vazia e define o índice de iteração como 0. Adicione um método :addSong(title) para adicionar músicas à playlist. O mais importante é implementar o metamétodo __call para que cada vez que a playlist for "chamada", ela retorne a próxima música da lista (ou nil quando não houver mais músicas).
  • main.lua: Importe seu módulo Playlist e crie uma nova playlist. Adicione músicas a ela com base nas entradas que você receber e, em seguida, use um loop for genérico para iterar pela playlist e imprimir cada título de música em sua própria linha.

A mágica do padrão Iterator é que seu objeto Playlist se torna chamável — quando usado em um loop for, o Lua o chama repetidamente até que ele retorne nil, fornecendo cada música em sequência.

Você receberá três entradas, cada uma representando um título de música para adicionar à sua playlist (em ordem).

Por exemplo, se as entradas forem Bohemian Rhapsody, Stairway to Heaven e Hotel California, a saída deve ser:

Bohemian Rhapsody
Stairway to Heaven
Hotel California

Se as entradas forem Yesterday, Imagine e Let It Be, a saída deve ser:

Yesterday
Imagine
Let It Be

Folha de consulta

O padrão Iterator permite que um objeto controle como seus dados internos são percorridos ao implementar uma lógica de iteração personalizada.

Use o metamétodo __call para tornar um objeto chamável como uma função. Cada chamada retorna o próximo item até que não haja mais nenhum (retorna 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

Com o __call definido, você pode usar o objeto diretamente em um loop for genérico:

local bag = Collection:new()
bag:add("apple")
bag:add("banana")

for item in bag do
    print(item)
end

O loop chama o objeto repetidamente. Quando o __call retorna nil, o loop para automaticamente.

Experimente você mesmo

-- Requer o módulo Playlist
local Playlist = require('Playlist')

-- Lê três títulos de músicas da entrada
local song1 = io.read()
local song2 = io.read()
local song3 = io.read()

-- TODO: Cria uma nova playlist usando Playlist:new()

-- TODO: Adiciona as três músicas à playlist usando :addSong()

-- TODO: Use um loop for genérico para iterar pela playlist
-- e imprimir cada título de música em sua própria linha
-- Dica: for song in playlist do ... end
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Object Oriented Programming