Lua code snippets

-- Mnemonic to avoid confusing pairs()/ipairs(). 
KEYS = pairs
KEYS_VALS = pairs

-- Looping: iterate over all keys/values of table t 
for k,v in KEYS_VALS(t) do body end

-- Looping: print all keys of table 
for k in KEYS(t) do end

-- Looping: iterate by ordinals of a table 
for i=1, #t do end

-- To separate/split words of a string: 
function SplitFields( str, sep )
    if ( str[string.len(str)] ~= sep ) then
        local str2 = string.format( "%s%s", str, sep )
        return {str2:match((str2:gsub("[^"..sep.."]*"..sep, "([^"..sep.."]*)"..sep)))}
    else
        return {str:match((str:gsub("[^"..sep.."]*"..sep, "([^"..sep.."]*)"..sep)))}
    end
end

fields = SplitFields( str, "," )
for key,word in KEYS_VALS(fields) do
    print( word )
end

constructors

What is strange about Lua is programmer must program mechanism for creating objects. Lua doesn't provide a constructor, programmer must write one.


chaining constructors across derivative classes

This is an example of building a class hierarchy in Lua. Unlike C++, chaining constructor functions has to be done explictly.

-- Produces a metatable (class table) that inherits methods from another one. 
function lib.DerivesFrom( BaseMT, t )
    local t = t or {}
    setmetatable( t, BaseMT )
    BaseMT.__index = BaseMT
    return t
end

-- Produces an "instance table" from a "class table" (metatable). 
-- For use in a Class:new() method. 
function lib.NewInstance( classTable, argTable )
    -- Construct instance table. 
    local instance = argTable or {}
    setmetatable( instance, classTable )
    classTable.__index = classTable
    return instance
end

Base = { }  -- ONLY ROOT BASE CLASS CAN BEGIN WITH AN EMPTY TABLE 

-- ctor. 
function Base:new( name )
    -- If base class is being instantiated, base class will be passed as self. 
    -- Or, derivative is being instantiated, which chains ctors by passing its instance as self. 
    if ( self == Base ) then
        self = lib.NewInstance( Base )  -- not chained, need new instance 
    end

    self.name = name

    return self
end

-- lib.DerivedClass() produces a "class table" that inherits from another "class table". 
-- Derived becomes a metatable, inheriting from Base, from which instance tables can be produced. 
Derived = lib.DerivesFrom( Base )

function Derived:new( name, value )
    -- Make instance table from derived metatable. 
    if ( self == Derived ) then
        self = lib.NewInstance( Derived )
    end

    Base.new( self, name )  -- chain to parent's ctor 

    self.value = value  -- derivative-specific 

    return self
end

pitfalls, traps

SERIOUS PITFALL: Lua's "if()" evalutes zero or empty string as TRUE!

Lua defines "if(expr)" as true unless expr is "false" or "nil".

"false" is a Lua keyword. Lua's notion of boolean differs from C++ and Python. Any defined object is true even if its value is zero or empty string.

val = 0;
if ( val )        # TRUE!!  Because val is neither "false" nor "nil"
if ( val ~= 0 )   # ok

Think of "if()" as meaning either "if defined" or "if true" in Lua. So then "val = 0; if ( val )" then makes sense as "val" is defined.

PITFALL: Write "~=" instead of "!="
 
PITFALL: 3 expressions between for/do will be evaluated only once:
for i=1, i < limit() do end  -- WRONG
PITFALL: Scope of these 3 expressions vanish after "end":
for i=1, i < n do end
if ( i == n ) -- WRONG: these are a different 'i' and 'n'
PITFALL: Tables are either an array or map:
table.insert( t, val )   -- becomes an ARRAY
t[key] = val             -- becomes a MAP

This loop only works if table was constructed as a MAP:
for k,v in KEYS_VALS(t) do
end
PITFALL: Remember Lua names are floating-point, do not think in integral terms:
--if ( (prg.tickCount % 50) == 0 ) then collectgarbage() end -- remember Lua number is fp
if ( (prg.tickCount % 50) < 1 ) then collectgarbage() end
PITFALL: A minor pitfall is that parentheses around a function call will truncate multiple return values (but such functions and writing this syntax is rarely done).
 

commentary/opinion

Lua is perfectly suited to embedding scripting capability into a C++ program. Its C implementation is tiny. Lua scripts are amazingly fast, fast enough to consider moving some C++ functions into Lua.