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.
Desafio
FácilVamos 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__callpara que cada vez que a playlist for "chamada", ela retorne a próxima música da lista (ounilquando 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 loopforgené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 CaliforniaSe as entradas forem Yesterday, Imagine e Let It Be, a saída deve ser:
Yesterday
Imagine
Let It BeFolha 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
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
1O Conceito 'Self'
Tabelas com Funções'self' ExplícitoA Sintaxe de Dois-pontosPonto vs Dois-pontosRecapitulação - Movendo o Ponto4Projeto: Banco Digital
Configuração do ProjetoMétodo de Depósito7Polimorfismo e Sobrescrita
Sobrescrita de MétodosChamando Métodos da Classe PaiDuck TypingInterface ComumVerificando o TipoRecapitulação - Papéis de Funcionários10Projeto: Gerenciador de Formas
Configuração do ProjetoClasse Retângulo2Padrão de Protótipo de Classe
O Conceito de ProtótipoVinculando com __indexO Construtor :new()Inicializando AtributosInstâncias IndependentesRecapitulação - Fábrica de Carros5Sobrecarga de Operadores em POO
Somando ObjetosSubtraindo ObjetosConcatenando ObjetosComparando Objetos (<, >)Recapitulação - Matemática de Carteira8Encapsulamento
Convenções de NomenclaturaClosures para PrivacidadeAcesso via ClosuresTabelas Somente LeituraLógica de ValidaçãoRecapitulação - Cofre Seguro11Padrões de Projeto (Lite)
Funções FactoryTabela SingletonPadrão IteratorObserver (Listener)Recapitulação - Logger Factory3Estado e Comportamento do Objeto
Variáveis de InstânciaMétodos GetterMétodos SetterPropriedades CalculadasFormatação de StringsVerificações de IgualdadeRecapitulação - Nota do Aluno6Fundamentos de Herança
Configurando a HerançaHerdando MétodosEstendendo o ConstrutorAdicionando Métodos FilhosCompartilhado vs ÚnicoRecapitulação - Hierarquia de Formas