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)
end

This prints:

Potato: 35 kg
false	true
true	false
Potato: 15 kg
Carrot: 20 kg
Pumpkin: 40 kg

The 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.)

  • __add is +. potatoes + morePotatoes calls Bag.__add(potatoes, morePotatoes), which returns a new bag with 35 kg; neither of the two bags changes. Others work the same way: __sub for -, __mul for *, __div for /.
  • __eq is == (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 __eq only when both sides are tables; a bag compared with a number is simply not equal.
  • __lt is < (less than). a > b is the same as b < a, so > uses __lt too. <= and >= need a metamethod of their own, __le; without it, they end with attempt to compare two table values.
  • table.sort compares items with <. With __lt, it sorts the bags without a comparison function, lightest first. Without __lt, table.sort(bags) ends with attempt to compare two table values, this time without main.lua: and a line number in front, because the error happens inside table.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 coins

Add __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
false

Hugo 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 coins

Start programming for free

By signing up, you agree to the Terms of Service and Privacy Policy.

Or sign up with:

7/9