Операторы
Объекты могут работать и с операторами. Обычно == между двумя таблицами проверяет, одна ли это таблица (как вы видели в уроке о таблицах как словарях, {} == {} равно false), а < или + между таблицами завершаются ошибкой. Метаметоды позволяют самим решить, что эти операторы делают с вашими объектами. Вот класс для мешков с собранным урожаем:
local Bag = {}
Bag.__index = Bag
function Bag.new(crop, kg)
local bag = {crop = crop, kg = kg}
setmetatable(bag, Bag)
return bag
end
function Bag:__tostring()
return self.crop .. ": " .. self.kg .. " кг"
end
function Bag.__add(a, b)
return Bag.new(a.crop, a.kg + b.kg)
end
function Bag.__eq(a, b)
return a.crop == b.crop and a.kg == b.kg
end
function Bag.__lt(a, b)
return a.kg < b.kg
end
local potatoes = Bag.new("Картофель", 20)
local morePotatoes = Bag.new("Картофель", 15)
local carrots = Bag.new("Морковь", 20)
print(potatoes + morePotatoes)
print(potatoes == carrots, potatoes == Bag.new("Картофель", 20))
print(morePotatoes < potatoes, morePotatoes > potatoes)
local bags = {Bag.new("Тыква", 40), carrots, morePotatoes}
table.sort(bags)
for _, bag in ipairs(bags) do
print(bag)
endОн выводит:
Картофель: 35 кг
false true
true false
Картофель: 15 кг
Морковь: 20 кг
Тыква: 40 кгМетаметод оператора получает обе стороны — a слева и b справа, а результатом оператора становится то, что он вернёт. Поэтому такие метаметоды определяют через точку и с двумя параметрами: обе стороны равноправны. (function Bag:__lt(other), где слева self, а справа other, была бы той же функцией.)
__add— это+.potatoes + morePotatoesвызываетBag.__add(potatoes, morePotatoes), которая возвращает новый мешок на 35 кг; ни один из двух мешков не меняется. Остальные работают так же:__subдля-,__mulдля*,__divдля/.__eq— это==(и~=, его противоположность). Здесь два мешка равны, когда в них одна и та же культура и одинаковый вес, хотя это разные таблицы. Lua вызывает__eqтолько когда обе стороны — таблицы; мешок, сравниваемый с числом, просто не равен ему.__lt— это<(меньше).a > b— то же, чтоb < a, поэтому>тоже использует__lt. Для<=и>=нужен собственный метаметод,__le; без него они завершаются с ошибкойattempt to compare two table values.table.sortсравнивает элементы через<. С__ltон сортирует мешки без функции сравнения, начиная с самого лёгкого. Без__ltвызовtable.sort(bags)завершается с ошибкойattempt to compare two table values, на этот раз безmain.lua:и номера строки в начале, потому что ошибка происходит внутриtable.sort. Строку, которая его вызвала, вы найдёте в трассировке под сообщением.
Типичная ошибка: применить оператор к тому, что не является объектом. potatoes + 5 вызывает Bag.__add(potatoes, 5), а b.kg для числа 5 завершается с ошибкой attempt to index a number value (local 'b').
Задание
- 1
Запустите программу:
table.sortне умеет сравнивать культуры. Добавьте вCropметаметод__lt, чтобы более дешёвая культура шла первой. Программа выводит:Морковь: цена 5 Картофель: цена 8 Клубника: цена 12 Тыква: цена 20 - 2
Добавьте
__eq: две культуры равны, когда у них одинаковые названия и одинаковые цены. В конце программы выведите, равна лиcrops[1]новой культуреCrop.new("Морковь", 5), а на следующей строке — равна лиcrops[2]культуреCrop.new("Картофель", 10):true false - 3
Гуго продаёт культуры ещё и наборами. Добавьте
__add, который делает из двух культур новую: её название — оба названия, соединённые через+, а цена — сумма обеих цен. В конце выведитеcrops[1] + crops[2], а затемcrops[1] + crops[2] + crops[3]. Второй вариант работает, потому чтоcrops[1] + crops[2]— тоже культура:Морковь + Картофель: цена 13 Морковь + Картофель + Клубника: цена 25