25. Modules and require — Homework solutions
Each problem has its own folder under
exercises/25/homework/solutions/. To run one:
cd exercises/25/homework/solutions/01-greet-module
lua main.lua
Problem 1 — Greet module
Problem. Finish a greet.lua module
exposing hello and bye.
How to think about it. Standard pattern: create
M, add functions, return M. Both are tiny —
one print each.
Worked solution. greet.lua:
local M = {}
function M.hello(name)
print("Hello, " .. name .. "!")
end
function M.bye(name)
print("Goodbye, " .. name .. ".")
end
return Mmain.lua:
local greet = require("greet")
greet.hello("Keiko")
greet.bye("Keiko")Common mistakes.
- Forgetting
return Mat the end.requirethen returnstrue, not the table, andgreet.hellofails with attempt to index a boolean value. - Running from the wrong directory.
cdinto the folder first.
Problem 2 — Math helpers
Problem. A module with double and
triple.
How to think about it. Same shape as problem 1, but
with maths instead of prints. M.double takes a
value and returns a new value — the caller does the
printing.
Worked solution. math_helpers.lua:
local M = {}
function M.double(x)
return x * 2
end
function M.triple(x)
return x * 3
end
return Mmain.lua:
local mh = require("math_helpers")
print(mh.double(7)) -- 14
print(mh.triple(7)) -- 21
print(mh.double(mh.triple(5))) -- 30Common mistakes.
printinside the module. Returning values lets the caller decide what to do with them.
Problem 3 — Counter
Problem. A module with increment,
get, and reset. The internal counter must
persist across calls.
How to think about it. A local at the
top of the file (outside any function) lives for the module's lifetime —
which, thanks to require's caching, is the whole program's
lifetime. All three exposed functions read and modify that same
variable.
Worked solution. counter.lua:
local M = {}
local count = 0
function M.increment()
count = count + 1
end
function M.get()
return count
end
function M.reset()
count = 0
end
return Mmain.lua:
local counter = require("counter")
counter.increment()
counter.increment()
counter.increment()
print(counter.get()) -- 3
counter.reset()
print(counter.get()) -- 0Common mistakes.
- Declaring
countinsideM.increment. Every call resets it to0, then bumps it to1. Persistent state must live outside the function. - Putting
countonM(i.e.M.count). That works, but lets every other file overwritecountdirectly. A module should expose only a controlled surface.
Challenge — String utilities
Problem. A module with three string functions of your own design.
How to think about it. Pick three. The example shows
shout, echo, and reverse_words.
The third splits on spaces by walking the string; if that feels too
much, swap in something simpler, like
M.exclaim(s) -> s .. "!".
Worked solution. string_utils.lua:
local M = {}
function M.shout(s)
return string.upper(s) .. "!"
end
function M.echo(s, n)
local parts = {}
for i = 1, n do
parts[i] = s
end
return table.concat(parts, " ")
end
function M.reverse_words(s)
local words = {}
for word in string.gmatch(s, "%S+") do
table.insert(words, 1, word)
end
return table.concat(words, " ")
end
return Mmain.lua:
local su = require("string_utils")
print(su.shout("hello")) -- HELLO!
print(su.echo("hi", 4)) -- hi hi hi hi
print(su.reverse_words("one two three four")) -- four three two onestring.gmatch is a built-in that walks every match of a
pattern in a string. %S+ means one or more
non-whitespace characters — a word.
table.insert(..., 1, word) puts each new word at position
1, pushing earlier ones along, so the list ends up reversed.
Common mistakes.
- Forgetting that
string.gmatchis fine to use even though it has not had its own section. Every built-in function is available the moment Lua starts.
Done?
Part 5 is finished once the four homework programs run. The Part 5 mini-project — a Text Adventure — combines functions, tables, and modules into a tiny game world.