Programming in Lua (5.0) Tutorial — Reference Manual — Short Reference — IBM intro: Embeddable scripting with Lua — Linux Journal intro: A Look at Lua
#!/usr/bin/lua
Use global table arg
local inp= io.stdin:read() -- Read a line from stdion print() -- Print a line to stdout io.stdout:write() -- Print a string to stdout io.stderr:write() -- Print a string to stderr
lua.vm.js thanks to asm.js
Luaj, seen at XPrivacyLua
This C code
d= a ? b : c
is in lua:
d= a and b or c
Note that this works only for single assignment. Use auxiliary tables for mulitiple assignment:
d1, d2 = table.unpack(a and {b1, b2} or {c1, c2})
nil or false. In this case the equivalent doesn't work. Instead use:
if a then d=b else d=c end
function foo(a) a= a or "defaultvalue" end
Take care, if false is a valid value, instead use:
function foo(a) if a==nil then a="defaultvalue" end end
function return_nil() return nil end function return_nothing() end function count_args(...) print(table.pack(...).n) end count_args(nil) --> 1 count_args() --> 0 --=> Passed nils increment argument count count_args(return_nil()) --> 1 count_args(return_nothing()) --> 0 --=> Returned nils increment return value count print(tonumber(nil)) --> nil print(tonumber(return_nil())) --> nil --=> tonumber() accepts nil as argument print(tonumber()) --> bad argument #1 to 'tonumber' (value expected) print(tonumber(return_nothing())) --> bad argument #1 to 'tonumber' (value expected) --=> tonumber() does not accept nothing as argument
local strict= require"fs.strict" local g,h,i local function f() g() -- ok h() -- ok --i() -- err, because i refers to old upvalue which is still nill end g= function() end -- existing upvalue g is assigned a function function h() end -- same as above (mind the missing "local"!) local function i() end -- a new upvalue is created which hides old i(assumption) f() i() -- ok
for i = 0,24,4 do -- start, end (including), step print(i) end
Use # operator
-- Iterate over array part: for i,v in ipairs(now) do print(i,v) end -- Iterate over complete table: for k,v in pairs(now) do print(k,v) end
pairs()
#!/usr/bin/lua -- Numbers: a=3 -- a contains 3 b=a -- b contains 3 a=4 -- a contains 4, b still contains 3 print(a,b) -- 4 3 -- Booleans: a=true; b=a; a=false print(a,b) -- false true -- Strings: a="World" -- a points to the string "World" b=a -- b points to the same string a="Hello" -- a points to "World", b still points to "Hello" print(a,b) --> Hello World -- Functions: a= function() print("World") end b=a -- a and b point to the same function: print(a,b) --> function: 0x80500e8 function: 0x80500e8 a= function() print("Hello") end -- a points to a new function and b still points to the old function: print(a,b) --> function: 0x8050a48 function: 0x80500e8 a() -- Hello b() -- World
#!/usr/bin/lua -- Tables: a={3} b=a; -- a and b point to the same table: print(a,b) -- table: 0x80509e0 table: 0x80509e0 print(a[1],b[1]) -- 3 3 a[1]=4; print(a[1],b[1]) -- 4 4 b[1]=5 print(a[1],b[1]) -- 5 5 a={6} -- a points to a new table and b still points to the old table: print(a,b) -- table: 0x8050a30 table: 0x80509e0 print(a[1],b[1]) -- 6 5 b[1]=7 print(a[1],b[1]) -- 6 7
pcall():
success: true, value(s) failure: false, error
Lua convention:
success: value(s) failure: nil, error
pcall() differs from error code after calling a function which complies to the convention!
pcall() converts an error raising function into an error returning function.
assert() or similar converts an error returning function into an error raising function.
| caller⇓ callee⇒ | return | raise |
|---|---|---|
| return | check | pcall() + check |
| raise | assert() | nothing |
A typical try {…}-block is done with a function in Lua. That means you end up having one function more for each try/catch usage
What to do when in doubt?
See also projects/accounting/sbaccimport which has since rev 482:c85c255f1870 classes with inheritance where the constructor new() is a class method and can therefore be reused in child classes similar to PIL capter 21
-- rectangle prototype: rectangle_proto= {species="rectangle"} function rectangle_proto:area() return self.width * self.height end -- rectangle constructor: function new_rectangle(width, height) local rectangle= {width= width, height= height} setmetatable(rectangle, {__index=rectangle_proto}) return rectangle end -- rectangle usage: rectangle= new_rectangle(3,4) print(rectangle:area()) --> 12 print(rectangle.species) --> rectangle -- circle prototype: circle_proto= {species="circle"} function circle_proto:area() return self.radius^2 * math.pi end -- circle constructor: function new_circle(radius) local circle= {radius= radius} setmetatable(circle, {__index=circle_proto}) return circle end circle= new_circle(1) print(circle:area()) --> 3.14 print(circle.species) --> circle
Classic single inheritance chain:
-- Inheritance: shape prototype: shape_proto= {family="shape"} function shape_proto:get_species() return self.species end function shape_proto:get_family() return self.family end setmetatable(rectangle_proto, {__index=shape_proto}) setmetatable(circle_proto, {__index=shape_proto}) print(circle:get_family().."/"..circle:get_species()) --> shape/circle print(rectangle:get_family().."/"..rectangle:get_species()) --> shape/rectangle
Lua print() does not look for a tostring() method directly in the table, instead it looks for a method called __tostring() in its metatable. Thus, to enable both print(myobject:tostring()) (explicit call) and print(myobject) (implicit call), provide the tostring() method in the prototype table of the object and set __tostring attribute of the metatable to that method:
-- rectangle prototype: rectangle_proto= {} function rectangle_proto:tostring() return "["..self.width.."|"..self.height.."]" end -- rectangle constructor: function new_rectangle(width, height) rectangle= {width= width, height= height} setmetatable(rectangle, { __index=rectangle_proto, __tostring=rectangle_proto.tostring }) return rectangle end -- rectangle usage: rectangle= new_rectangle(3,4) print(rectangle:tostring()) --> [3|4] print(rectangle) --> [3|4]
This works also if tostring() is defined in the base clase:
-- shape as base class: shape_proto= {} function shape_proto:tostring() return "["..self.width.."|"..self.height.."]" end -- rectangle prototype inherits from shape: rectangle_proto= {} setmetatable(rectangle_proto, { __index=shape_proto }) -- rectangle constructor: function new_rectangle(width, height) rectangle= {width= width, height= height} setmetatable(rectangle, { __index=rectangle_proto, __tostring=rectangle_proto.tostring }) return rectangle end -- rectangle usage: rectangle= new_rectangle(3,4) print(rectangle:tostring()) --> [3|4] print(rectangle) --> [3|4]
local M= {} local private= {} -- holds private data of all objecs in subtables -- Class Point: -- local Point_proto= {} -- Class prototype function Point_proto:set(x,y) local priv= private[self] priv.x= x priv.y= y end function Point_proto:get() local priv= private[self] return priv.x, priv.y end function Point_proto:tostring() local priv= private[self] return "["..priv.x.."|"..priv.y.."]" end Point_proto.__index = Point_proto Point_proto.__newindex= function() assert(false,"object is write-protected") end setmetatable(Point_proto, { -- ensure read protection for Point and all childs __index= function() assert(false, "object is read-protected") end }) function M.new_Point(x,y) -- constructor local instance= {} setmetatable(instance, Point_proto) private[instance]= {} -- create private part of object instance:set(x,y) return instance end return M
#!/usr/bin/lua local point= require "point" local print_r= function(table) for key,value in pairs(table) do io.write("[", key, "] = ", tostring(value), "\n") end end local p= point.new_Point(3,4) --p.x= 8 --> Write access throws an error --print(p.y) --> Read access throws an error print(p:tostring()) --> [3|4] print_r(p) --> prints nothing
local M= {} local private= {} -- holds private data of all objecs in subtables -- Class Point: -- -- ... -- CLASS MovablePoint: -- local MovablePoint_proto= {} -- Class prototype function MovablePoint_proto:move(dx, dy) local priv= private[self] priv.x= priv.x+dx priv.y= priv.y+dy end MovablePoint_proto.__index = MovablePoint_proto MovablePoint_proto.__newindex= Point_proto.__newindex setmetatable(MovablePoint_proto, Point_proto) -- Inherit from Point function M.new_MovablePoint(x,y) -- constructor local instance= M.new_Point(x,y) -- call parent constructor setmetatable(instance, MovablePoint_proto) return instance end return M
#!/usr/bin/lua local point= require "point" -- ... local mp= point.new_MovablePoint(5,6) mp.x= 9 --> Write access throws an error print(mp.y) --> Read access throws an error print(mp:tostring()) --> [5|6] mp:move(2,1) print(mp:tostring()) --> [7|7] print_r(mp) --> prints nothing
Each of the following constructs is equivalent, it reads a file line by line:
local f= io.open"file.txt" repeat local l= f:read("l") if l then print(l) end until not l f:close()
local f= io.open"file.txt" for l in f:lines("l") do print(l) end f:close()
for l in io.lines("file.txt", "l") do print(l) end
„l“ as arg for read() is the default, it can be omittedfile:lines() and io.lines() accept the same format args as read(). (Tested. Nowhere found in docu). Thus you could read a file eg chunk by chunk as well.#!/usr/bin/lua local separator=":" for line in io.lines() do print(line) for cell in line:gmatch("[^"..separator.."]+") do print("", cell) end end
To run some code sitting in a different file, use dofile()
To load global variables of an external (config) file into a table locally, you have to pass the table as environment:
a="Harry" b="Hirsch"
#!/usr/bin/lua local extenv={} local chunk= loadfile("config.lua", "t" , extenv) chunk() for k,v in pairs(extenv) do io.write("[", k, "] = ", tostring(v), "\n") end
[a] = Harry [b] = Hirsch
See ModuleDefinition and LuaStyleGuide → Modules
module() is deprecated!
now= os.time()
-- Return a table containing hour, min, wday, day, ... of current time: now = os.date("*t") -- Return a table containing hour, min, wday, day, ... of a certain time: atime = os.date("*t", timestamp) -- Return a formated string of current time: now = os.date("Today is %A") -- Today is Thursday
do var= 0 for i=1,102000000 do var= var+1 end end -- real 0m4.516s
do local var= 0 for i=1,102000000 do var= var+1 end end -- real 0m1.003s
local var= 0 do for i=1,102000000 do var= var+1 end end --real 0m0.970s
do local var= 0 local closure= function() for i=1,102000000 do var= var+1 end end closure() end -- real 0m1.666s
do local tab= {var= 0} for i=1,102000000 do tab.var= tab.var+1 end end -- real 0m4.248s
do local tab= {0} for i=1,102000000 do tab[1]= tab[1]+1 end end -- real 0m4.767s
Tested with Lua 5.2
table.concat()
See also:
A newly created coroutine is in state supended:
co1= coroutine.create(function() end) print(coroutine.status (co1)) --> suspended
A coroutine which resumes another coroutine goes from state running to normal while the resumed couroutine goes from state suspended torunning:
co1= coroutine.create(function() print("in co1: state main:", coroutine.status(main)) print("in co1: state co1", coroutine.status(co1)) end) main= coroutine.running() -- get handle to main coroutine print("in main: state main:", coroutine.status(main)) print("in main: state co1:", coroutine.status(co1)) coroutine.resume(co1)
Result: in main: state main: running in main: state co1: suspended in co1: state main: normal in co1: state co1 running
A running couroutine can yield() back to its resumer, which puts the resumer back to running and itself back to suspended:
co1= coroutine.create(function() print("in co1: state main:", coroutine.status(main)) print("in co1: state co1", coroutine.status(co1)) coroutine.yield() end) main= coroutine.running() -- get handle to main coroutine coroutine.resume(co1) print("in main: state main:", coroutine.status(main)) print("in main: state co1:", coroutine.status(co1))
Result: in co1: state main: normal in co1: state co1 running in main: state main: running in main: state co1: suspended
Alternatively a running couroutine can resume another coroutine (which is in state suspended):
co1= coroutine.create(function() coroutine.resume(co2) end) co2= coroutine.create(function() print("in co2: state main:", coroutine.status(main)) print("in co2: state co1", coroutine.status(co1)) print("in co2: state co2", coroutine.status(co2)) end) main= coroutine.running() -- get handle to main coroutine coroutine.resume(co1)
Result: in co2: state main: normal in co2: state co1 normal in co2: state co2 running
But a running couroutine cannot resume a non-suspended coroutine. Only coroutines in state suspended can be resumed:
co1= coroutine.create(function() local res, err= coroutine.resume(main) print(res, err) end) main= coroutine.running() -- get handle to main coroutine coroutine.resume(co1)
Result: false cannot resume non-suspended coroutine
#!/usr/bin/lua co= coroutine.create(function() while true do io.stderr:write('co: waking up\n') local inp= io.stdin:read() io.stderr:write('co: read "'..inp..'", going to sleep\n') coroutine.yield(inp) end end) while true do io.stderr:write('main: going to sleep\n') local state, oup= coroutine.resume(co) io.stderr:write('main: waking up\n') print('> '..oup) end