This is a Tips & Tricks question with the purpose of letting people accumulate their patterns, tips and tricks for Lua.
Lua is a great scripting language, however there is a lack of documented patterns, and I'm sure everyone has their favorites, so newcomers and people wondering if they should use it or not can actually appreciate the language's beauty.
Swap variables:
a, b = b, a
If a is nil, copy b's value:
a = a or b
let a, b = b, a
- Stringer
Metatables are sexy, but depending on the language you're coming from they may take a while to wrap your head around.
The first thing you need to realize is that Lua has one data structure - the table - and everything is an entry in a table. Functions, strings, everything.
So when you call a function, or look something up in an array, or whatever, you're actually telling Lua to look up a key value pair in a table. The neat thing is that with metatables you can tell Lua what to do if the key isn't found. You can either point it at a different table or alternatively a function.
This opens up all sorts of neat tricks. You can create a lookup table of English to German translations - and if you ever try to translate something that isn't in the table it can print an apology, display the untranslated string, and/or log an error to let the developers know they're missing a translation. Sure you can DO that in other languages, but you end up with messy branching conditionals. With Lua it's transparent.
Say you've got a table of translations called tranMsgs:
setmetatable(tranMsgs,tranFail)
tranFail.__index = function(t,k)
-- code goes here
end
Now whenever anyone tries to lookup a missing translation the above code will run. Very simple, very understandable.
You can also use metatables to cache complex calculations.
cachedResults = {}
setmetatable(cachedResults, expensiveFunction)
expensiveFunction.__index = function(t,k)
cachedResults.k = sqrt(sqrt(k^k))
return cachedResults.k
end
Now calling print(cachedResults[50]) will be much faster after the first time. And sure you can do that in any language, but it's brief, elegant, and easy to understand in Lua. :-)
If you're coming from a language like C or Java, getting your head around metatables will open the door to a lot of fun tricks.
The Lua compiler optimizes tail calls into something resembling a goto. This allows for an interesting way to express a Finite State Machine where the individual states are written as functions and state transitions are performed by tail-calling the next state.
Coming from a background where tail calls are not optimized it looks a bit like recursion gone wild, of course.
-- Define State A
function A()
dosomething"in state A"
if somecondition() then return B() end
if done() then return end
return A()
end
-- Define State B
function B()
dosomething"in state B"
if othercondition() then return A() end
return B()
end
-- Run the FSM, starting in State A
A()
The example above is more than a little contrived, but it shows the key features. State transitions take the form "return NewState() end" and exiting the FSM completely is done by returning anything other than a tail call from any state.
Just make sure that you really are using tail calls, or you will get recursion gone wild after all... in particular
-- bad example!
function C()
D() -- note no return keyword here
end
does not tail call D. Instead, C explicitly returns nothing after calling D, which required a stack frame. In effect,
-- bad example!
function E()
E()
end
will eventually overflow the stack, but
-- infinite loop hiding in the tail call
function F()
return F()
end
will loop forever.
The 'metafib' example by Cody Hatch is not quite correct (or, at least, the comment explaining it is not). There is no recursion going on, in fact, the code given is very inefficient: whenever a fibonacci number is requested that is not yet known, the entire sequence is recomputed from the start.
A version that does use recursion (not using __call
but __index
, so you get the n-th number by using [n] instead of (n)):
fibotable = {[1] = 0, [2] = 1}
setmetatable(fibotable, {
__index = function (t, i)
t[i] = t[i-1]+t[i-2]
return t[i]
end
})
print(fibotable[11])
If you try to get number '11' from the table, and it is not yet there, the __index
metamethod will be executed. This method will calculate the requested number by adding the 10th and 9th numbers together (for which, potentially, __index
will be called again), and then store it in the array. If you were to print the entire table after the last print statement, you would see it contained all the fibonacci numbers up to and including 11.
Here is a little class module I wrote with the purpose of looking nice (syntactic sugar). It's not a full feature system, but does serve as an interesting paradigm for making a user-friendly class system in Lua:
mt_class ={}
function mt_class:inherit(...)
local parents = {...}
self.__parents = self.__parents or {}
local k,parentname
for k,parentname in ipairs(parents) do
table.insert(self.__parents,getfenv(1)[parentname])
end
return self
end
function class(name)
local env = getfenv(1)
local __classname = name
local builder = {}
local define= function(builder,proto)
env[__classname]=proto
local __parents = builder.__parents
local find =function(self,key)
local result = proto[key]
if not result and __parents then
local k,parent
for k,parent in ipairs(__parents) do
result = parent[key]
if result then break end
end
end
return result
end
function proto.new(instance)
assert(instance~=proto)
local instance = instance or {}
return setmetatable(instance,{__index = find})
end
end
return setmetatable(builder,{__index=mt_class,__call=define})
end
And it's usage is quite elegant:
class "Dog" {
say="Bowow"
}
class "Rover":inherit "Dog" {
middle ="Rover"
}
r = Rover.new{likes="slippers"}
print(r.say)
print(r.name)
print(r.likes)
So there you go, defining classes is easy with an API like this.
One of the biggest pain when debugging is the lack of function for printing a table. Here is a function I use to do that :
function dump(o)
local s = ''
if type(o) == 'table' then
s = '{ '
for i = 1, #o do
s = s .. dump(o[i])
if i ~= #o then s = s .. ', ' end
end
s = s .. ' }'
else
s = tostring(o)
end
return s
end
Of course you can enhance it, but I find it very useful.
lua-users wiki: Sample Code [1]:
[1] http://lua-users.org/wiki/SampleCodeFollowing is a list of pages with sample code. Types of code include standalone reusable functions/classes, reusable modules, example code illustrating the use of other modules, tutorials on using other modules, full programs, and design patterns.
It is a good thing to stimulate Stack Overflow community to share their favorite Lua snippets, but, in my opinion, the lack of documented patterns is overemphasized.
For example, please see most excellent books "Lua Programming Gems" [1] and "Programming in Lua" [2]. Also do not forget Lua wiki [3]
[1] http://rads.stackoverflow.com/amzn/click/8590379841Seemingly little-known syntactic sugar:
foo = {a=1, b=2}
function foo:test(c)
print(self.a, c)
end
foo:test(3) --prints: 1 3
Declaring it as foo:test
instead of foo.test
adds an implicit first parameter named self
. Calling foo:test(3)
is the same as calling foo.test(foo,3)
. You can use this for OOP, creating new instances of foo like so:
myFoo = {}
for k,v in pairs(foo) do myFoo[k] = v end
myFoo.a = 27
myFoo:test(8) --prints: 27 8
Note that simply myFoo = foo
won't work as you'll be creating a reference rather than a copy.
Beware if using this with libraries such as LuaGnome; if you did myWidget:connect("clicked", myFoo.test, myFoo)
, you'd end up getting the event as self
and myFoo as a
. In this case you have to declare test as function foo.test(a, self)
since GTK is not aware of the self
parameter.
(Disclaimer: Shameless self-plug ahead)
MiddleClass [1] is a small (124 LOC) library that you can use for object orientation in Lua.
require('MiddleClass')
local MyClass = class('MyClass')
function MyClass:initialize(x,y,z)
super.initialize(self)
self.x, self.y, self.z = x,y,z
end
function MyClass:foo(x,y,z)
return self.x+x, self.y+y, self.z+z
end
MiddleClass supports single inheritance and (a rudimentary form of) mix-ins.
There's also a middleclass-extras [2] project with some utility tables implementing stateful classes, callback handling and signaling, among others.
[1] http://github.com/kikito/middleclassHere is a Y combinator [1] in Lua:
function Y(f)
local function recurse(...)
return f(recurse, ...)
end
return recurse
end
factorial = Y(function(factorial, n)
if n == 1 then
return 1
else
return factorial(n - 1) * n
end
end)
print(factorial(5)) --> 120
[1] http://stackoverflow.com/questions/93526/what-is-a-y-combinatorShort if-else:
C
int var = condition ? 1 : 2;
Lua
local var = condition and 1 or 2
Example
print(nil and 'true' or 'false') -- prints false
print(1 and 'true' or 'false') -- prints true
print(1 and false or 2)
, print(1 and nil or 2)
would both print 2
. It's misleading to present such expressions as replacement to if-else
. - katspaugh
One can have any function call an arbitrary metatable function like so:
function toclass(obj)
if getmetatable(obj).__toclass then
return getmetatable(ob).__toclass(obj)
else
--fancy to class logic here...
end
end
Here's a small Lua based unittest framework defined in Lua:
unittest = {
test= {
equal = function(a,b)
return a == b
end,
notequal = function(a,b)
return a ~= b
end,
exception = function(a,...)
--//do a protected call kinda like Lua's try/catch
local ok, err = pcall(a,...)
if not ok then
return true
end
return false
end,
noexception = function(a,...)
--//do a protected call kinda like Lua's try/catch
local ok, err = pcall(a,...)
if not ok then
return false
end
return true
end,
},--//end of tests
--//start unit testing environment
start = function()
--//store the calling environment (=closure)
ut = { oldenv = getfenv(2) }
--//allow new environment to index the global environment
setmetatable (ut,{__index = _G})
setfenv(2, ut)
end,
stop = function()
--//Return the environment to what it used to be, unscratched
setfenv(2, getfenv(2).oldenv)
end
}
--//declare some global variable
a = 25
--//the code I want to test in a protected enviroment
do --//if you run this from the commandline you "do" to create a closure
unittest.start()
a = 1 --//oops! someone redefined my global variable!!!
print(unittest.test.equal(a,1))
print(unittest.test.notequal(a,2))
print(unittest.test.exception(aFunctionThatDoesntExist,10))
print(unittest.test.noexception(math.sin,19))
print(a) --// 1
unittest.stop()
end
--//luckily the test enviroment contained all damage to the global table!
print(a) --//25
I use this for unit-testing but you can also use this to define safe environments to avoid having user code redefine your applications base code
Here's an elegant way to compute a Fibonacci series with metatables, which I suspect is what Thomas was thinking of:
fibotable = {[1] = 0, [2] = 1}
setmetatable(fibotable, {
__index = function (t, i)
t[i] = t[i-1]+t[i-2]
return t[i]
end
})
print(fibotable[11])
(Stolen from Jan De Vos's post)
What's happening here is that when you try to check the 11th number in the series it checks to see if it's been calculated. If not it tries to do so by adding the 10th and 9th numbers - which requires checking to see if they've been calculated yet. If either or both of them haven't been calculated yet it recurses until it hits something which has been calculated, then it calculates all the needed numbers, storing them as it goes so that they'll be cached for future lookups and calculations. It's conceptually very elegant, I think.
print(fibotable[-1])
- finnw
This is a global metatable trick I use to define useful aliases when using Lua, the cool thing is the aliases are actually getting the results from functions not from static data, my favorite is "now" as an alias to get the current system time.
system_mt = { --this table cannot be local, because the __index goes on recursively
now = function()
return os.time()
end,
homepath = function()
return os.getenv("HOMEPATH")
end,
hostname = function()
local socket = socket or require"socket"
if socket then
return socket.dns.gethostname()
else
return "LOCALHOST"
end
end,
source = function()
local t = debug.getinfo(3,'S')
return t.source
end,
__index = function(t,k)
if system_mt[k] then --Global variables
return system_mt[k]()
end
end,
}
setmetatable(_G,system_mt)
stamp = now
print(now)
print(source)
print(localhost)
dofile(homepath.."/myscripts/test.lua")
print(now-stamp)
Here is a benign technique for deprecating functions from a larger code base:
--// Useful wrapper function for printf
function printf(s,...)
print(string.format(s,...))
end
--//deprecated metatable
deprecate_mt = {
__index = function(t,k)
printd(t.__deprecate_msg)
return t.__deprecate_table[k]
end,
__newindex = function(t,k,v)
printd(t.__deprecate_msg)
t.__deprecate_table[k] = v
end,
}
--//Use functional programming to redefine the function
--//or table as a new one that wraps the old one in a deprecated metatable
function deprecate(o,msg)
local msg = msg or "deprecated call" --//Using Lua's closure abilities to store msg
if type(o) == "function" then --//functions are first class citizens in Lua
return function(...)
--//returning a function that wraps the deprecated function, this is functional programming at it's best
printf("deprecated[%s]",msg)
return o(...)
end
else
local dmt = getmetatable(o) or {}
dmt.__index = function(t,k)
printf(t.__deprecate_msg)
return t.__deprecate_table[k]
end
dmt.__newindex = function(t,k,v)
printf("deprecated[%s]",t.__deprecate_msg)
t.__deprecate_table[k] = v
end
dt = {
__deprecate_msg = msg,
__deprecate_table = o,
}
setmetatable(dt,dmt)
return dt
end
end
function foo()
print("foo")
end
function bar()
print("bar")
end
foo()
foo = deprecate(foo,"foo has been deprecated use bar instead")
foo() --//"foo has been deprecated use bar instead" \n "foo"
oldtable={"hello"}
oldtable = deprecate(oldtable, "oldtable has been deprecated use newtable instead")
print(oldtable[1]) --// "oldtable has been deprecated use newtable instead" \n "hello"
A table merging and a table copying function:
-- thanks to http://stackoverflow.com/questions/1283388/lua-merge-tables/1283608#1283608 and
-- http://stackoverflow.com/questions/1397816/changing-one-table-seems-to-change-the-other/1400485#1400485
-- tableMerge:
-- merges two tables, with the data in table 2 overwriting the data in table 1
function tableMerge(t1, t2)
for k,v in pairs(t2) do
if type(v) == "table" then
if type(t1[k]) ~= "table" then -- if t1[k] is not a table, make it one
t1[k] = {}
end
tableMerge(t1[k], t2[k])
else
t1[k] = v
end
end
return t1
end
-- tableCopy:
-- takes a table and returns a complete copy including subtables.
function tableCopy(t)
return tableMerge({}, t)
end
I just whipped up a set of code for a simple "iowriter" class that can help in writing data to files. You need the LOOP (Lua Object Oriented Programming) library, but you already have it if you use the default install of Lua.
require 'loop.simple'
class = loop.simple.class
classof = loop.simple.classof
initclass = loop.simple.initclass
instanceof = loop.simple.instanceof
isclass = loop.simple.isclass
memberof = loop.simple.memberof
members = loop.simple.members
new = loop.simple.new
rawnew = loop.simple.rawnew
subclassof = loop.simple.subclassof
superclass = loop.simple.superclass
local open = {}
ioWriter = class{
stream = false
}
ioWriter[open] = false
function ioWriter:open(name)
if not self[open] then
self.stream = io.open(name, "w")
self[open] = true
else
error("attempted to open an already open writer")
end
end
function ioWriter:write(str)
self.stream:write(str)
end
function ioWriter:writeLine(str)
self.stream:write(str .. '\n')
end
function ioWriter:close(self)
if self[open] then
self.stream:flush()
self.stream:close()
self.stream = false
self[open] = false
else
error("attempted to close an already closed writer")
end
end
Example script:
require 'ioWriter.lua'
local writer = iowriter()
writer:open("test.txt")
writer:write("this is a test, ")
writer:writeLine("this is the second test")
writer:close()
The most useful part about this is that it provides enough simplification to wrap your own more specialized implementation around this, like an HTML or XML writing class.
Suggestions for improvement are welcome!
clean.seeall
is an alternative to package.seeall
You normally use it with module this way when declaring modules:
module(..., clean.seeall)
<rest of module code>
...
With package.seeall, there is a big drawback: The module now mirrors the global environment, (for example yourmodule.print == print
) (
Lua Module Function Critiqued
[1])
With clean.seeall
, the module environment inherits the global environment, but it does not expose it to the outside.
local clean = {}
_G[(...) or "clean"] = clean
-- Called as module(..., clean.seeall)
-- Use a private proxy environment for the module,
-- so that the module can access global variables.
-- + Global assignments inside module get placed in the module
-- + Lookups in the private module environment query first the module,
-- then the global namespace.
function clean.seeall(_M)
local priv = {} -- private environment for module
local privmt = {}
privmt.__index = function(priv, k)
-- debug the "global" namespaces: print(_M._NAME .. "." .. k)
return _M[k] or _G[k]
end
privmt.__newindex = _M
setmetatable(priv, privmt)
setfenv(3, priv)
end
[1] http://lua-users.org/wiki/LuaModuleFunctionCritiquedreturn _M[k] or _G[k]
but there is a bug that I haven't noticed despite using this for a whole application: false values. So it needs to be rewritten with explicit if/else clauses. :-( - kaizer.se
Not a super tricky item, but one that I use over and over:
If you miss the compactness of C's conditional operator: '?', for example:
printf("The operation %s", status ? "succeeded":"failed");
Then in Lua you might like to use a function I defined as 'iif' (for inline-if) so that you can in Lua do this:
print "The operation "..iif(status,"succeeded","failed")
Nice and compact. The iif() function is simply:
function iif(cond,tru,fls) --inline if, instead of C's (cond ? tru:fls)
if cond then
return tru
else
return fls
end
end
function iff(cond,tru,fls) cond and tru or fls end
- benzado
iif(x,x[1],42)
will raise an exception if x is nil. But it won't if you use x and x[1] or 42
- Scott S
Elegant and simple string lambda expression generator:
-- file: lambda.lua //inspired by MetaLua
local function lambda_from_str(f)
if f:find '^%s*[a-zA-Z_][a-zA-Z0-9_]*|.*' then
local name,rest = f:match '^%s*([a-zA-Z_][a-zA-Z0-9_]*)(|.*)'
if not name then return nil, 'bad string lambda (rest ='.. rest ..')'end
local args,body = rest:match '%s*|%s*([^|]*)%s*|%s*(.+)%s*'
if not args then return nil, 'bad string lambda (body ='.. body ..')'end
return 'local ' .. name .. ';'
.. name .. '= function(' .. args:gsub('%s*', '') ..')'
.. lambda_from_str(body) .. ' end; return ' .. name
elseif f:find '^%s*|' then
local args,body = f:match '%s*|%s*([^|]*)%s*|%s*(.+)%s*'
if not args then return nil, 'bad string lambda (body ='.. body ..')'end
return 'return function(' .. args:gsub('%s*','') .. ')'
.. lambda_from_str(body) .. ' end'
elseif f:find '^%s*{.+}%s*$' then
local body = f:match '^%s*{%s*(.+)%s*}%s*$'
if not body then return nil, ('bad lambda string ("'.. f ..'")') end
return body
else return 'return ' .. f end
end
local cache = {}
return function (str_fun)
local str_fun_ok = lambda_from_str(str_fun)
if cache[str_fun_ok] then return cache[str_fun_ok]
else
local y = loadstring(str_fun_ok)()
cache[str_fun_ok] = y
return y
end
end
Usage:
L = require 'lambda'
local sum = L'|a, b| a + b'
print( ' 2 + 3 = ' .. sum(2, 3) )
-- 2 + 3 = 5
local sum_curried = L'|a| |b| a + b'
print( ' 3 + 4 = ' .. sum_curried(3)(4) )
-- 3 + 4 = 7
local add1 = sum_curried(1)
local add_one = (L'|a| |b| a + b')(1)
print( add1(2) .. ' = ' .. add_one(1+1) )
-- 3 = 3
local factorial = L'f|n|n == 0 and 1 or n*f(n-1)'
local fibonacci = L'f|n|n<2 and n or f(n-1)+f(n-2)'
print( 'fact(170) == '.. factorial(170) ..', fib(33) == '.. fibonacci(33) ..'\n')
-- fact(170) == 7.257415615308e+306, fib(33) == 3524578
printf = L'|f,...|print(string.format(f, ...))'
now = L'||os.time()'
now_stop = L'|beg| printf("Time diff: %s seconds\\n", now() - beg)'
start = now()
num = factorial(333)
num1 = fibonacci(36)
now_stop(start)
-- Time diff: 3 seconds
A simple function wrapper to execute as a coroutine:
function cororun (func)
local f = coroutine.wrap (func)
f (f)
end
With this wrapper, you can call an asynchronous function as if it's a synchronous function. Use this like so:
cororun (
function (f)
some_asynchronous_function (f) -- pass wrapped function as a callback
local val = coroutine.yield ()
another_asynchronous_function (f)
local another_val = coroutine.yield ()
end)
Note that f
always points the function itself.
If you want to protect a metatable but still have it readable (for say being able to access and call metamethods) you can do something like this:
local mt = {}
local t = { string = "foo"}
mt.__tostring = function(self) return self.string end
mt.__metatable = mt
setmetatable(t, mt)
local bar = gemetatable(t).__tostring(t) -- should work
setmetatable(t, mt) -- won't work
The difference between else if
and elseif
. If you have
if foo then
--do something...
elseif bar then
--do something else...
end
it's roughly equivalent to
if foo then
--do something
else
if bar then
--do something else
end
end
Fine, unless you have multiple else if statements (where do the extra elses
go?). Plus, there's not enough end's.
A function that rounds whole numbers and a function that rounds numbers to the specified number of decimal places:
-- round:
-- takes a number and returns a rounded up or down version
-- if number is 0.5, it rounds up
function round(num)
local floor = math.floor(num)
local ceiling = math.ceil(num)
if (num - floor) >= 0.5 then
return ceiling
end
return floor
end
-- decRound:
-- takes a number and the number of places to round to and returns a suitably rounded number
function decRound(num, placesToRound)
local multipler = tonumber("1" .. string.rep("0", placesToRound), 10)
num = num * multipler
num = round(num)
num = num / multipler
return num
end
they could probably be compressed into one function, but i'll leave that as an exercise to the reader.
function round(n) return math.floor(n+0.5) end
- kikito
A function to truncate a number and to truncate a number to the specified number of decimal places:
-- truncate:
-- given a number, returns a number with X max digits
function truncate(num, count)
local numString = tostring(num)
numString = string.sub(numString, 0, count)
return 0 + numString -- forces conversion to number
end
-- decTruncate:
-- given a number with a decimal place and the number of decimal places to truncate to, returns a number truncated to the specified number of decimal places.
function decTruncate(num, decimalPlaces)
local wholeNumString = tostring(math.floor(num))
return truncate(num, decimalPlaces + string.len(wholeNumString) + 1)
-- we're adding 1 because we have to take the decimal point into account in truncation
end
string.format
for this? e.g. 0 + string.format("%"..decimalPlaces.."f", num)
- finnw
Another simple trick that's not commonly seen in code, but totally awesome is:
=("foo"):upper() ->output: FOO
However this actually is very dangerous if you run unsafe code because you can access the global string module from any string, so malicious code could do something like:
getmetatable("foo").__index.upper = function() print("bye bye sucker");os.exit() end
=("foo"):upper() >output: bye bye sucker (application quits)