Operators
Objects can work with operators too. Normally, == between two tables asks whether they're the same table (as you saw in the lesson on tables as dictionaries, {} == {} is false), and < or + between tables ends with an error. Metamethods let you decide what these operators do with your objects. Here's a class for bags of harvested crops:
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 .. " 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("Potato", 20)
local morePotatoes = Bag.new("Potato", 15)
local carrots = Bag.new("Carrot", 20)
print(potatoes + morePotatoes)
print(potatoes == carrots, potatoes == Bag.new("Potato", 20))
print(morePotatoes < potatoes, morePotatoes > potatoes)
local bags = {Bag.new("Pumpkin", 40), carrots, morePotatoes}
table.sort(bags)
for _, bag in ipairs(bags) do
print(bag)
endThis prints:
Potato: 35 kg
false true
true false
Potato: 15 kg
Carrot: 20 kg
Pumpkin: 40 kgThe metamethod of an operator gets both sides, a from the left and b from the right, and the result of the operator is whatever it returns. That's why these metamethods are defined with a dot and two parameters: both sides are equal partners. (function Bag:__lt(other), with self on the left and other on the right, would be the same function.)
__addis+.potatoes + morePotatoescallsBag.__add(potatoes, morePotatoes), which returns a new bag with 35 kg; neither of the two bags changes. Others work the same way:__subfor-,__mulfor*,__divfor/.__eqis==(and~=, its opposite). Here, two bags are equal when they hold the same crop and weigh the same, even though they're different tables. Lua calls__eqonly when both sides are tables; a bag compared with a number is simply not equal.__ltis<(less than).a > bis the same asb < a, so>uses__lttoo.<=and>=need a metamethod of their own,__le; without it, they end withattempt to compare two table values.table.sortcompares items with<. With__lt, it sorts the bags without a comparison function, lightest first. Without__lt,table.sort(bags)ends withattempt to compare two table values, this time withoutmain.lua:and a line number in front, because the error happens insidetable.sort. You'll find the line that called it in the traceback below the message.
Typical mistake: using an operator with something that isn't an object. potatoes + 5 calls Bag.__add(potatoes, 5), and b.kg on the number 5 ends with attempt to index a number value (local 'b').
Instructions
Run the program: table.sort doesn't know how to compare crops. Add __lt to Crop, so that a cheaper crop comes first. The program prints:
Carrot: 5 coins
Potato: 8 coins
Strawberry: 12 coins
Pumpkin: 20 coinsAdd __eq: two crops are equal when they have the same name and the same price. At the end of the program, print whether crops[1] equals a new crop Crop.new("Carrot", 5), and on the next line, whether crops[2] equals Crop.new("Potato", 10):
true
falseHugo also sells crops in bundles. Add __add, which makes a new crop out of two: its name is both names joined with + , and its price is the sum of both prices. At the end, print crops[1] + crops[2], and then crops[1] + crops[2] + crops[3]. The second one works because crops[1] + crops[2] is a crop too:
Carrot + Potato: 13 coins
Carrot + Potato + Strawberry: 25 coinsStart programming for free
7/9