Padrão Iterator
Parte da seção Programação orientada a objetos do Journey de Lua da Coddy. Lição 64 de 70.
Quando você tem uma coleção de itens armazenada dentro de um objeto, muitas vezes quer percorrê-los em um loop. O loop genérico for do Lua funciona com qualquer função iteradora: ele chama a função antes de cada iteração e para quando a função retorna nil.
O padrão Iterador fornece ao objeto um método que retorna uma função desse tipo. A função mantém sua posição em uma variável local, então a posição pertence a um loop, não à coleção:
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 chamada a bag:iter() cria uma nova função com seu próprio index, começando em 0. A função é uma closure: ela ainda pode usar index e self depois que iter retorna. Depois do último item, self.items[index] é nil, e o loop para:
local bag = Collection:new()
bag:add("apple")
bag:add("banana")
for item in bag:iter() do
print(item) -- apple, depois banana
end
for item in bag:iter() do
print(item) -- apple, depois banana novamente
endMantenha a posição fora da própria coleção. Se o objeto armazenasse self.index e atuasse como seu próprio iterador, o primeiro loop deixaria o índice no final, então um segundo loop não imprimiria nada, e um loop interrompido antes do fim com break faria o próximo loop começar no meio.
Desafio
FácilVamos criar uma classe Playlist que você pode percorrer com o loop genérico for do Lua! Você vai implementar o padrão Iterator com um método :iter() que retorna uma função iteradora.
Você vai 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 de um método:addSong(title)que adiciona uma música a ela. Mais importante, adicione um método:iter()que retorna uma nova função cada vez que é chamado. Essa função mantém sua posição em uma variável local e retorna a próxima música a cada chamada, 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 recebidas e, em seguida, use um loop genéricoforsobreplaylist:iter()para imprimir o título de cada música em sua própria linha.
Como cada chamada a :iter() começa na primeira música, a mesma playlist pode ser percorrida quantas vezes você quiser.
Você receberá três entradas, cada uma representando o título de uma música a ser adicionada à sua playlist (na ordem).
Por exemplo, se as entradas forem Bohemian Rhapsody, Stairway to Heaven e Hotel California, a saída deverá ser:
Bohemian Rhapsody
Stairway to Heaven
Hotel CaliforniaSe as entradas forem Yesterday, Imagine e Let It Be, a saída deverá ser:
Yesterday
Imagine
Let It BeExperimente você mesmo
-- Carregue o módulo Playlist
local Playlist = require('Playlist')
-- Leia três títulos de músicas da entrada
local song1 = io.read()
local song2 = io.read()
local song3 = io.read()
-- TODO: Crie uma nova lista de reprodução usando Playlist:new()
-- TODO: Adicione as três músicas à lista de reprodução usando :addSong()
-- TODO: Use um laço for genérico com playlist:iter()
-- e imprima cada título de música em uma linha separada
-- Dica: for song in playlist:iter() 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 Programação orientada a objetos
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ânguloClasse CírculoMétodo PerímetroColeção de FormasÁrea TotalFiltrar Formas2Padrã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 FormasPratique por conta própria: Compilador de Lua online