share
Stack OverflowLua Patterns,Tips and Tricks
[+56] [28] Robert Gould
[2008-09-18 02:37:17]
[ design-patterns lua patterns tips-and-tricks ]
[ http://stackoverflow.com/questions/89523] [DELETED]

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.

(10) Why has this been closed as "not constructive" ? This question has elicited responses containing facts, references, and especially examples of specific expertise. If you feel the question elicits too broad a variety of answers then mark it accordingly. "Not constructive" is the wrong category. - maengle
[+30] [2008-09-18 02:44:41] Leonardo Constantino

Swap variables:

a, b = b, a

If a is nil, copy b's value:

a = a or b

(2) +1 for the swap. FYI, Python and other languages support this syntax as well. Very Convenient! - Justin Ethier
(1) for your second example, will also apply if a is false. - daurnimator
(3) have you ever needed variable swapping?? - arkilus
I agree it's not common but when you need it there's a nice syntax backing it up. - Leonardo Constantino
(1) Functional languages like OCaml can do that too thanks to shadowing and pattern matching. let a, b = b, a - Stringer
1
[+27] [2008-09-18 03:46:15] Cody Hatch

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.


your cachedResults example does not work, and is poorly written (eg, using cachedResults instead of t in your __index function). Also, this approach is much more complicated for functions that take more than one value. - daurnimator
see lua-users.org/wiki/FuncTables for a good overview of memoizing. - daurnimator
@daurnimator: no, it isn't. you can pack arguments - Grozz
@Grozz Huh? that wiki page gives a good overview; and in your code it should be cachedResults[k] (not cachedResults.k); otherwise you cannot pack arguments; as the index will fire every time (tables with same contents do not hash the same). - daurnimator
2
[+19] [2009-02-19 05:49:23] RBerteig

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.


3
[+17] [2008-09-18 09:11:22] Jan de Vos

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.


(2) You are absolutely correct. I misread what the for loop was doing and...well...yeah. Embarrassing. And your example is MUCH cleaner too. - Cody Hatch
4
[+10] [2009-03-17 11:10:25] Robert Gould

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.


5
[+8] [2009-01-19 17:30:14] Wookai

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.


(6) A number of alternative implementations is listed here: lua-users.org/wiki/TableSerialization - Alexander Gladysh
6
[+8] [2008-09-29 05:12:33] Eugene Yokota

lua-users wiki: Sample Code [1]:

Following 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.

[1] http://lua-users.org/wiki/SampleCode

7
[+8] [2009-01-29 07:29:47] Alexander Gladysh

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/8590379841
[2] http://rads.stackoverflow.com/amzn/click/8590379825
[3] http://lua-users.org/wiki/SampleCode

Exactly, and great books. - Sébastien RoccaSerra
Indeed but this post began before Gem's was released ;) - Robert Gould
(1) Anyways I agree with you to a point, but there are some things here that you can't find in any of the those sources, so the thread's got a value of its own. - Robert Gould
Err, yes, sorry. Is see now that this is an old thread. It just popped up in my RSS reader. Although I'd referenced those Lua books and wiki in the question itself ;) - Alexander Gladysh
8
[+7] [2010-04-10 13:54:54] Rena

Seemingly 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.


9
[+5] [2010-10-11 07:01:27] kikito

(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/middleclass
[2] http://github.com/kikito/middleclass-extras

(3) MiddleClass is awesome... I'm pretty new to LUA... this has just rocked my world. - Jasconius
(1) ^^ glad you liked it. Let me know if you need any help! - kikito
10
[+5] [2009-01-29 05:17:17] Robert Gould

Here 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-combinator

11
[+4] [2010-08-11 19:41:17] arkilus

Short 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
12
[+3] [2009-08-12 05:06:34] RCIX

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

13
[+3] [2008-09-18 11:28:59] Robert Gould

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


14
[+3] [2008-09-18 03:53:37] Cody Hatch

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.


(1) I´m sorry but I don´t find that to be any elegant at all... Specially if you compare that algorithm to similar pieces in functional languages. - Vicent Marti
Tanoku - you were right. The solution in my post now is much more elegant, I think. :-) - Cody Hatch
print(fibotable[-1]) - finnw
15
[+3] [2008-09-18 04:14:15] Robert Gould

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)

16
[+2] [2008-09-18 04:36:47] Robert Gould

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"

17
[+2] [2009-09-15 14:10:34] RCIX

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

18
[+2] [2009-08-06 04:01:23] RCIX

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!


You could add a sample of usage? Although I (and other Lua buffs) can see how this should work, it's not clear for newcomers how to use your writer class - Robert Gould
As soon as i can i'll update my sample to fix a bug i found and imrove functionality a little, along with adding an example. - RCIX
19
[+2] [2010-05-05 19:43:29] kaizer.se

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/LuaModuleFunctionCritiqued

haven't tested this one yet, but it does seem like a great idea! Thanks for sharing - Robert Gould
The lookup function is written beautifully as return _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
20
[+2] [2010-09-04 14:31:55] proFromDover

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

(1) In most cases, this will work for you and doesn't require a user-built function: status and "succeeded" or "failed" - sworoc
In other words, function iff(cond,tru,fls) cond and tru or fls end - benzado
(1) The post's implementation misses out on the most important feature of the ternary operator - short-circuiting. e.g. 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
21
[+1] [2011-08-25 01:32:24] DinGODzilla

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

22
[+1] [2010-09-03 01:52:27] torus

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.


23
[+1] [2009-08-12 06:37:52] RCIX

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

24
[0] [2009-08-13 01:51:06] RCIX

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.


25
[0] [2009-09-15 14:14:23] RCIX

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.


for round, you can do function round(n) return math.floor(n+0.5) end - kikito
26
[0] [2009-09-15 14:16:45] RCIX

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

Wouldn't it be easier to use string.format for this? e.g. 0 + string.format("%"..decimalPlaces.."f", num) - finnw
@finnw: Possibly... - RCIX
27
[0] [2008-11-28 08:56:37] Robert Gould

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)

(3) You may protect yourself from this as described in here: stackoverflow.com/questions/325323/… - Alexander Gladysh
28