Iteradores personalizados
Parte da seção Programação orientada a objetos do Journey de GDScript da Coddy. Lição 33 de 65.
Python faz uma classe se comportar como uma lista com métodos mágicos como __len__, __getitem__, __contains__ e __iter__. Godot tem métodos virtuais para apenas uma dessas tarefas: o loop for. Três métodos fazem for item in my_object: funcionar:
_iter_init(iter)é executado uma vez, antes da primeira iteração. Ele define a posição inicial e retornatruese houver um primeiro elemento._iter_get(position)retorna o elemento na posição atual, e a variável do loop recebe esse valor._iter_next(iter)é executado antes de cada iteração seguinte. Ele avança para a próxima posição e retornafalsequando não há mais elementos, encerrando o loop.
# team.gd
class_name Team
extends RefCounted
var _members: Array[String] = []
func add(member: String) -> void:
_members.append(member)
func _iter_init(iter: Array) -> bool:
iter[0] = 0
return iter[0] < _members.size()
func _iter_next(iter: Array) -> bool:
iter[0] += 1
return iter[0] < _members.size()
func _iter_get(position):
return _members[position]iter é um Array com uma posição que o Godot cria para cada loop. iter[0] contém a posição desse loop, e o Godot passa seu valor para _iter_get(). Agora, um Team funciona em um loop for:
var team := Team.new()
team.add("Ada")
team.add("Bo")
for member in team:
print(member) # Ada, depois BoMantenha a posição em iter[0], não em uma variável membro como _position. Cada loop tem seu próprio iter, então dois loops sobre o mesmo time, mesmo que um esteja dentro do outro, não interferem um no outro. Com uma variável membro, o loop interno moveria a posição do loop externo.
As outras operações de lista não têm método virtual: len(team) e team[0] são erros. Escreva métodos comuns com nomes como os de Array, por exemplo, size(), get_at(index) e has(member). Não teste a pertinência com in: "Ada" in team verifica se Team tem uma propriedade ou método chamado Ada, portanto o resultado é false.
Desafio
FácilEscreva a classe Bookshelf em bookshelf.gd. Ela mantém os títulos em _titles e comporta no máximo _capacity livros:
add(title)adiciona o título e retornatrue, ou retornafalsesem adicionar quando a estante está cheia ou já contém esse título.size()retorna o número de livros, ehas(title)informa se o título está na estante.get_at(index)retorna o título nesse índice ou uma string vazia quando o índice está fora da estante._iter_init(),_iter_next()e_iter_get()permitem que um loopforpercorra os títulos na ordem em que foram adicionados.
O código fornecido lê a capacidade, o número de títulos e um título por linha, e informa os títulos que não conseguiu adicionar. Ele imprime o número de livros, lista-os com um loop for pela estante, depois lê mais um título e imprime se a estante o contém e qual é o último livro. Para a capacidade 3, os títulos Dune, Emma, Dune, Ulysses, Hamlet e a busca Emma, ele imprime:
Could not add: Dune
Could not add: Hamlet
Books: 3
1. Dune
2. Emma
3. Ulysses
Has Emma: true
Last: UlyssesSeu código deve ficar em bookshelf.gd. main.gd contém o código fornecido de entrada/saída e não pode ser editado.
Experimente você mesmo
extends Node
func _ready():
# Código de entrada/saída fornecido: mantenha-o como está
var shelf := Bookshelf.new(int(OS.read_string_from_stdin()))
var count := int(OS.read_string_from_stdin())
for i in count:
var title := OS.read_string_from_stdin()
if not shelf.add(title):
print("Could not add: " + title)
print("Books: %d" % shelf.size())
var number := 1
for title in shelf:
print("%d. %s" % [number, title])
number += 1
var query := OS.read_string_from_stdin()
print("Has %s: %s" % [query, shelf.has(query)])
print("Last: " + shelf.get_at(shelf.size() - 1))
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
1Fundamentos da POO
Arquivos externosIntrodução à POOClasses vs. objetosA palavra-chave selfMétodosPropriedadesConstrutor (_init)Revisão - Calculadora simples4Polimorfismo
Revisitando a Sobrescrita de MétodosDuck TypingClasses e Métodos AbstratosDesign de InterfacesRevisão - Calculadora de Formas7Sinais
O que é um sinal?Declarando e emitindoConectando-se a sinaisSinais com argumentosRevisão - Sistema de saúde10Padrões de Projeto Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão Composite2Propriedades de Classe
Variáveis de Instância vs. EstáticasFunções EstáticasMétodos Setters de PropriedadeMétodos Getters de PropriedadePrivado por ConvençãoRecapitulando: Gerenciador de Conta Bancária5Encapsulamento
Membros públicos e privadosValidação com settersOcultação de informaçõesPropriedades somente para leituraRevisão - Sistema de registros de alunos8Conceitos Avançados de POO
Composição vs. HerançaClasses InternasNós como ComponentesRefCounted vs. NodeEnums em Classes3Herança
Herança básicaA palavra-chave superSobrescrita de métodosCadeias de herançaVerificações de tipo com isRevisão - Hierarquia de funcionários6Métodos especiais
Introdução aos métodos virtuaisO método _to_stringIteradores personalizadosRevisão - Lista personalizada9Padrões de Projeto Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonPadrão FactoryPadrão ObserverPadrão StrategyPratique por conta própria: Compilador de GDScript online