Menu
CoddyTech

Пользовательские итераторы

Часть раздела Объектно-ориентированное программирование путешествия по GDScript на Coddy. Урок 33 из 65.

Python позволяет классу вести себя как список с помощью магических методов, таких как __len__, __getitem__, __contains__ и __iter__. В Godot есть виртуальные методы только для одной из этих задач — цикла for. Три метода обеспечивают работу for item in my_object::

  • _iter_init(iter) выполняется один раз перед первым проходом. Он задаёт начальную позицию и возвращает true, если есть первый элемент.
  • _iter_get(position) возвращает элемент в текущей позиции, и переменная цикла получает это значение.
  • _iter_next(iter) выполняется перед каждым следующим проходом. Он перемещает на следующую позицию и возвращает false, когда элементов больше нет, завершая цикл.
# 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 — это массив с одним элементом, который Godot создаёт для каждого цикла. iter[0] хранит позицию этого цикла, и Godot передаёт её значение в _iter_get(). Теперь Team работает в цикле for:

var team := Team.new()
team.add("Ada")
team.add("Bo")
for member in team:
	print(member) # Ada, затем Bo

Храните позицию в iter[0], а не в переменной-члене, такой как _position. Каждый цикл получает собственный iter, поэтому два цикла по одной и той же команде, даже если один вложен в другой, не мешают друг другу. При использовании переменной-члена внутренний цикл переместил бы позицию внешнего цикла.

Для остальных операций со списком нет виртуального метода: len(team) и team[0] вызывают ошибки. Напиши обычные методы с такими же именами, как у Array, например size(), get_at(index) и has(member). Не проверяй наличие элемента с помощью in: "Ada" in team проверяет, есть ли у Team свойство или метод с именем Ada, поэтому результат — false.

challenge icon

Задание

Легко

Напиши класс Bookshelf в файле bookshelf.gd. Он хранит названия в _titles и может содержать не более _capacity книг:

  • add(title) добавляет название и возвращает true либо возвращает false, не добавляя его, если полка заполнена или такое название уже есть на полке.
  • size() возвращает количество книг, а has(title) — есть ли такое название на полке.
  • get_at(index) возвращает название по этому индексу или пустую строку, если индекс находится за пределами полки.
  • _iter_init(), _iter_next() и _iter_get() позволяют циклу for перебирать названия в порядке их добавления.

Предоставленный код считывает вместимость, количество названий и по одному названию в каждой строке, а затем сообщает, какие названия не удалось добавить. Он выводит количество книг, перечисляет их в цикле for по полке, затем считывает ещё одно название и выводит, есть ли оно на полке, а также последнюю книгу. Для вместимости 3, названий Dune, Emma, Dune, Ulysses, Hamlet и поиска Emma он выводит:

Could not add: Dune
Could not add: Hamlet
Books: 3
1. Dune
2. Emma
3. Ulysses
Has Emma: true
Last: Ulysses

Твой код должен быть в файле bookshelf.gd. В main.gd находится предоставленный код ввода/вывода, и его нельзя редактировать.

Попробуйте сами

extends Node

func _ready():
	# Предоставленный код ввода-вывода: оставьте его без изменений
	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))
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор GDScript