Конструкторы
Морковь из предыдущего упражнения — одна таблица, написанная вручную. Но на ферме много культур: морковь, картофель, тыквы… Записывать каждую вместе со всеми методами значило бы писать один и тот же код снова и снова. Вместо этого напишите функцию, которая их создаёт. Функция, которая создаёт новый объект и возвращает его, называется конструктором:
local function newCrop(name, days)
local crop = {name = name, days = days, age = 0}
function crop:grow()
self.age = self.age + 1
end
function crop:describe()
print(self.name .. ": день " .. self.age .. " из " .. self.days)
end
return crop
end
local carrot = newCrop("Морковь", 3)
local pumpkin = newCrop("Тыква", 7)
carrot:grow()
carrot:grow()
pumpkin:grow()
carrot:describe()
pumpkin:describe()Он выводит:
Морковь: день 2 из 3
Тыква: день 1 из 7Каждый вызов newCrop выполняет её код заново: создаёт новую таблицу, кладёт в неё два метода и возвращает её. carrot и pumpkin — две отдельные таблицы, поэтому когда растёт морковь, тыква не меняется. А так как методы используют self, а не фиксированное имя, один и тот же код подходит для обеих.
В {name = name, days = days, age = 0} одно и то же слово встречается дважды — для конструкторов это обычное дело. Слева от = стоит ключ, справа — значение, здесь это параметр с тем же именем. Поэтому newCrop("Морковь", 3) создаёт {name = "Морковь", days = 3, age = 0}.
Классическая ошибка: забыть return. Без return crop конструктор создаёт таблицу, но не отдаёт её, поэтому переменная получает nil:
local function newCrop(name, days)
local crop = {name = name, days = days, age = 0}
function crop:grow()
self.age = self.age + 1
end
end
local carrot = newCrop("Морковь", 3)
carrot:grow()Программа завершается с ошибкой:
main.lua:10: attempt to index a nil value (local 'carrot')Ошибка указывает на строку, где вы впервые используете объект, а не на конструктор. Если новый объект оказался nil, проверьте return в конструкторе.
Одна проблема. Каждый вызов newCrop заново создаёт и методы: строки с function crop:grow() выполняются снова и делают новую функцию. grow у моркови и grow у тыквы делают одно и то же, но это две разные функции:
local function newCrop(name)
local crop = {name = name, age = 0}
function crop:grow()
self.age = self.age + 1
end
return crop
end
local carrot = newCrop("Морковь")
local pumpkin = newCrop("Тыква")
print(carrot.grow == pumpkin.grow)Он выводит:
falseДля трёх культур это неважно. Но на поле из 1000 культур с десятью методами у каждой набралось бы 10 000 функций, которые на самом деле копии одних и тех же десяти. Лучше хранить методы в одном месте, общем для всех культур, а каждой культуре оставить только её собственные данные: название, срок и возраст. Для этого и нужны метатаблицы — о них следующее упражнение.
Задание
- 1
У Эммы кур больше, чем одна Златка. Превратите код, который создаёт Златку, в конструктор: локальную функцию
newHen(name), которая создаёт курицу с заданным именем и 0 яиц, даёт ей оба метода и возвращает её. Затем создайте Златку черезlocal goldie = newHen("Златка"). Программа выводит то же, что и раньше. - 2
Под Златкой с помощью
newHenсоздайте вторую курицу — Перлу. Перла сносит 3 яйца, а потом ещё 1. Выведите её отчёт под отчётом Златки:Златка снесла яиц: 2 Перла снесла яиц: 4 - 3
Проверьте, что в этом упражнении сказано о методах: в конце программы выведите
goldie.lay == pearl.lay. Программа выводит:Златка снесла яиц: 2 Перла снесла яиц: 4 false