Constructors
The carrot from the previous exercise is one table, written out by hand. But a farm has many crops: carrots, potatoes, pumpkins… Writing out each one with all its methods would mean the same code again and again. Instead, write a function that makes them. A function that creates a new object and returns it is called a constructor:
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 .. ": day " .. self.age .. " of " .. self.days)
end
return crop
end
local carrot = newCrop("Carrot", 3)
local pumpkin = newCrop("Pumpkin", 7)
carrot:grow()
carrot:grow()
pumpkin:grow()
carrot:describe()
pumpkin:describe()This prints:
Carrot: day 2 of 3
Pumpkin: day 1 of 7Every call of newCrop runs its code again: it creates a new table, puts the two methods into it, and returns it. carrot and pumpkin are two separate tables, so when the carrot grows, the pumpkin doesn't change. And because the methods use self, not a fixed name, the same code works for both.
In {name = name, days = days, age = 0}, the same word appears twice, which is usual in constructors. On the left of = is the key, on the right is the value, here the parameter of the same name. So newCrop("Carrot", 3) creates {name = "Carrot", days = 3, age = 0}.
The classic mistake: forgetting return. Without return crop, the constructor creates the table but doesn't give it back, so the variable gets 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("Carrot", 3)
carrot:grow()The program ends with an error:
main.lua:10: attempt to index a nil value (local 'carrot')The error points to the line where you first use the object, not to the constructor. When a new object is nil, check the constructor's return.
One problem. Each call of newCrop also creates the methods again: the lines with function crop:grow() run again and make a new function. The carrot's grow and the pumpkin's grow do the same thing, but they're two different functions:
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("Carrot")
local pumpkin = newCrop("Pumpkin")
print(carrot.grow == pumpkin.grow)This prints:
falseFor three crops, that doesn't matter. But a field of 1,000 crops with ten methods each would hold 10,000 functions that are copies of the same ten. It would be better to keep the methods in one place, shared by all crops, and let each crop hold only its own data: its name, days and age. That's what metatables are for, and they're the topic of the next exercise.
Instructions
Emma has more hens than just Goldie. Turn the code that creates Goldie into a constructor: a local function newHen(name) that creates a hen with the given name and 0 eggs, gives it both methods, and returns it. Then create Goldie with local goldie = newHen("Goldie"). The program prints the same as before.
Below Goldie, use newHen to create a second hen, Pearl. Pearl lays 3 eggs, and then 1 more. Print her report below Goldie's:
Goldie laid 2 eggs
Pearl laid 4 eggsCheck what this exercise says about methods: at the end of the program, print goldie.lay == pearl.lay. The program prints:
Goldie laid 2 eggs
Pearl laid 4 eggs
falseStart programming for free
3/9