Functional Style
Prerequisites: Closures, Advanced tables
Lua supports a functional style well: functions are values, closures are cheap, and tail calls don't grow the stack.
Composition and partial application
local function compose(...)
local fns = table.pack(...)
return function(...)
local results = table.pack(...)
for i = fns.n, 1, -1 do
results = table.pack(fns[i](table.unpack(results, 1, results.n)))
end
return table.unpack(results, 1, results.n)
end
end
local function partial(fn, a)
return function(...) return fn(a, ...) end
end
local trim = function(s) return (s:gsub("^%s+", ""):gsub("%s+$", "")) end
local slug = compose(function(s) return (s:gsub("%W+", "-")) end, string.lower, trim)
print(slug(" Hello Lua World "))
local add = function(a, b) return a + b end
local add10 = partial(add, 10)
print(add10(5))
hello-lua-world
15
Proper tail calls
A call in the form return f(...) is a tail call: Lua reuses the
current stack frame, so tail recursion never overflows:
local function countdown(n)
if n == 0 then return "liftoff" end
return countdown(n - 1)
end
print(countdown(1000000))
local function sum(n, acc)
acc = acc or 0
if n == 0 then return acc end
return sum(n - 1, acc + n)
end
print(sum(1000000))
liftoff
500000500000
Only return f(args) exactly is a tail call — return f(args) + 1 or
return (f(args)) isn't.
Memoization
local function memoize(fn)
local cache = {}
return function(n)
if cache[n] == nil then cache[n] = fn(n) end
return cache[n]
end
end
local fib
fib = memoize(function(n)
if n < 2 then return n end
return fib(n - 1) + fib(n - 2)
end)
print(fib(80))
23416728348467685
Without the cache, fib(80) would take longer than the age of the
universe; with it, it's instant.
What's next
Try the Intermediate Projects, then continue to Level 3 — Advanced: environments, performance, embedding, and Lua in games, editors, and servers.