26. Methods and self — Homework solutions
The .lua solution files are in
exercises/26/homework/solutions/.
Problem 1 — Counter object
Problem. A counter table with inc,
get, and reset methods.
How to think about it. The table holds the state in
count; each method reads or changes
self.count. The colon definition and call do the
wiring.
Worked solution.
local counter = { count = 0 }
function counter:inc()
self.count = self.count + 1
end
function counter:get()
return self.count
end
function counter:reset()
self.count = 0
end
counter:inc()
counter:inc()
counter:inc()
print(counter:get()) -- 3
counter:reset()
print(counter:get()) -- 0Common mistakes.
- Writing
function counter.inc(self)— the expanded form works, but reads as if you forgot the colon. Pick one style. - Calling
counter.inc()instead ofcounter:inc(). Without the colon,selfisniland the assignment fails.
Problem 2 — Dog with bark
Problem. Two dog tables with their own names, each
calling its own :bark().
How to think about it. Two tables, each with a
name field, can share the same method, or each can
define its own bark. Both work; the second wastes memory
and is rarely worth it.
Worked solution.
local function makeDog(name)
local dog = { name = name }
function dog:bark()
print(self.name .. ": Woof!")
end
return dog
end
local rex = makeDog("Rex")
local lassie = makeDog("Lassie")
rex:bark() -- Rex: Woof!
lassie:bark() -- Lassie: Woof!A simpler version without the helper function:
local rex = { name = "Rex" }
function rex:bark()
print(self.name .. ": Woof!")
end
local lassie = { name = "Lassie" }
function lassie:bark()
print(self.name .. ": Woof!")
end
rex:bark()
lassie:bark()Both give the same output. Chapter 27 generalises the first into a proper class.
Common mistakes.
- Sharing one
barkmethod by writinglassie.bark = rex.bark. That works — it printsLassie: Woof!becauseself.namereads from whichever table the call started on — and is a valid optimisation. Just know what you are doing.
Problem 3 — Calculator object
Problem. A calc table with arithmetic methods that update an internal value.
Worked solution.
local calc = { value = 0 }
function calc:add(n)
self.value = self.value + n
end
function calc:sub(n)
self.value = self.value - n
end
function calc:mul(n)
self.value = self.value * n
end
function calc:show()
print("Value: " .. self.value)
end
calc:add(10)
calc:show() -- Value: 10
calc:mul(3)
calc:show() -- Value: 30
calc:sub(5)
calc:show() -- Value: 25Common mistakes.
- Returning the new value from each method instead of updating
self.value. Both are fine, but the spec asks for in-place updates, so the methods modifyself.value.
Challenge — Stack
Problem. A LIFO stack with push,
pop, peek, size.
Worked solution.
local stack = { items = {} }
function stack:push(v)
table.insert(self.items, v)
end
function stack:pop()
return table.remove(self.items)
end
function stack:peek()
return self.items[#self.items]
end
function stack:size()
return #self.items
end
stack:push("a")
stack:push("b")
stack:push("c")
print(stack:size()) -- 3
print(stack:peek()) -- c
print(stack:pop()) -- c
print(stack:pop()) -- b
print(stack:size()) -- 1Without a position, table.insert and
table.remove work at the end of the list — the right place
for stack operations.
Common mistakes.
- Using
table.insert(self.items, 1, v)andtable.remove(self.items, 1). That inserts at the front — a queue, not a stack — and costs more because every other item shifts. - Forgetting to return from
popandpeek. The default return isnil, which silently breaks the test.
Done?
The next chapter takes this pattern further. With metatables, you stop writing the same boilerplate for every "class" and share method tables across many instances.