00:00
vrld @ GPN12
Wo Lua?
- Datenformat.
- Config Dateien.
- Skripte & Plugins.
- Kleber zwischen Modulen.
- Spiele: LÖVE, MOAI, Corona, …
- Webkram: Kepler Project.
- Eingebettete Systeme: eLua.
- Neuerdings Malware: Flame
- Guckt mal hier und hier.
Warum Lua?
- Lua ist simpel.
- Lua ist einfach.
- Lua ist konsistent.
- Lua ist flexibel.
- Korollar: Lua passt sich an.
- Lua ist schnell.
- Lua läuft fast überall.
Zuweisung
- Zuweisung ändert den Wert einer Variable
foo = 1
foo = 2
foo = {}
foo.bar = 1
- Mehrfachzuweisung
foo, bar = 1, 2
foo, bar = bar, foo
print(foo, bar) --> 2 1
a, b, c = 1, 2
print(a, b, c) --> 1 2 nil
a, b, c = 3, 4, 5, 6, 7
print(a, b, c) --> 3 4 5
Werte und Typen
- Variablen sind typlos!
- Werte tragen Typ mit sich
- Lua ist dynamisch typisiert
type(val)
liefert Typ
nil
type(nil) = "nil"
nil == nil
nil ~= alles andere
- Abwesenheit eines Wertes
number
type(1) = "number", type(3.14) = "number"
- Double precision floating point (
double
)
- Arithmetik:
+, -, *, /, %, ^
print(1+1, -2, 2-1, 3*4) --> 2 -2 1 12
print(3/4, 4%3, 2^8) --> 0.75 1 256
print(math.pi % 3, math.pi - math.pi % .01)
--> 0.14159265358979 3.14
- Relationen:
<, >, <=, >=, ==, ~=
print(3 < 4, 3 > 4) --> true false
print(4 ~= nil) --> true
string
type("hi") = "string"
"Hallo", 'Welt', [[multiline]], [=[string]=]
- 8-Bit clean, eingebettete
'\0'
möglich
- Länge eines
string
s: #
s = "Fischers Fritz\0fischt frische Fische"
print(s, #s) --> Fischers Fritz 36
- Verkettung:
..
print('Hello' .. 'World') --> HelloWorld
a, b = [[Hello]], [=[World]=]
print(a .. " " .. b) --> Hello World
string
s sind immutable
table
type({foo = 'bar'}) = "table"
- Assoziativer Array
- Jeder Wert außer
nil
kann als Index genutzt werden
- Die Datenstruktur in Lua
- Array
number
als Index
- Objekt
string
als Index
- Menge
- Wert als Index und Eintrag
- Graph
- Verschachtelte
table
s
- Modul
table
s
- ...
table
s!
table
erstellen
t = {}
t[1] = 'foo'
t['bar'] = 1
t.baz = {}
t[t] = t
print(t[1]) --> foo
t[1] = nil
print(t[1]) --> nil
s = { 'foo', 2, 3,
['bar'] = 1;
baz = {};
[s] = s, --< Fehler! s = nil
}
print(s[2], s.baz) --> 2 table: 0x19f6570
table
als Array
- Indizes bei 0 oder 1 oder -1 anfangen
- Konvention: Startindex ist 1
- Länge eines Arrays:
#array
- Array-Idiome
array[#array] = nil -- letztes element löschen
array[#array+1] = 'foo' -- element anfügen
- Vorsicht bei Arrays mit Löchern
array = {1, nil, 2}
print(#array) --> 3
array[6] = 3 -- array = {1, nil, 2, nil, nil, 3}
print(#array) --> 1 ???
print(table.maxn(array)) --> 6
function
type(type) = "function"
- Definieren und Aufrufen
function foo() print('bar') end
foo() --> bar
- Argumente und Rückgabewerte
function bar(a,b) return a+b end
print(bar(1,2)) --> 3
- First class citizen
foo = function() print('bar') end
_print = print
function print(a) _print(a, a*a, a^a) end
print(3) --> 3 9 27
Mehr
thread
- Kooperative Threads
- Später mehr…
userdata
- Beliebige Daten
- Keine vordefinierten Operationen
if then else
if a < 0 then a = 0 end
if op == '+' then
print(a+b)
elseif op == '-' then
print(a-b)
elseif op == '*' then
print(a*b)
elseif op == '/' then
print(a/b)
else
error("Invalid operation: " .. op)
end
case
?
- Nicht vorhanden
- Tables to the rescue!
foo = ({
case1 = 1,
case2 = 2,
})[bar]
- We can do better (später)
foo = case (bar) {
case1 = 1;
case2 = 2;
default = 0
}
Numerisches for
- Syntax
for var = from,to,inc do <something> end
- Zum Beispiel...
for i = 1,10 do print(i) end
for i = 1,#a/2 do
a[i],a[#a-i+1] = a[#a-i+1], a[i]
end
for i = #a,1,-1 do
if not filter(a[i]) then
table.remove(a, i)
end
end
Generisches for
- Syntax
for <varlist> in <explist> do <something> end
- Fast immer
tbl = {foo = 1, bar = 2, baz = 3}
for k,v in pairs(tbl) do print(k,v) end
--> bar 3; bar 2; ...
a = {'a','b','c','d'}
for i,v in ipairs(a) do print(v) end
--> 1 a; 2 b; ...
for line in io.lines(path) do
do_something(line)
end
break
und return
break
beendet umschließende Schleife
p = list
while p do
if something(p) then break end
p = p.next
end
return
gibt Werte zurück
function add(a,b) return a+b end
-- the module
return {
add = function(a,b) return a+b end
}
Zucker
- Optionale Klammern
print "string"
local module = require 'module'
v = vector{x = 10, y = 20}
type{}
self
object = {a = 0}
function object.foo(self, a) print(a) end
function object:bar(a) self.a = self.a + a end
object:foo('Hallo self') --> Hallo self
object.bar(object, 1) -- object.a = 1
object:bar(2) -- object.a = 3
Higher Order Functions
- Funktionen als Argumente
function map(tbl, f)
for k,v in pairs(tbl) do tbl[k] = f(v,k) end
return tbl
end
t = map({1, 2, 3}, function(v) return v*v end)
- Funktionen als Rückgabewerte
function s1(x) return x == '0' and s2 or s1 end
function s2(x) return x == '0' and s1 or s2 end
s = s1
for c in ('00011100'):gmatch('.') do s = s(c) end
print(s == s1) --> false
Mehrere Rückgabewerte
- Funktionen können mehrere Werte zurückgeben
function foo(a,b) return a+b, a-b, a*b, a/b end
- Gotcha
function vadd(a,b, c,d) return a+c, b+d end
vadd(vadd(1,1, 2,1), 2,3) -- attempt to perform
-- arithmetic on local 'd' (a nil value)
- Anwendung
f, error_message = io.open(path, 'r')
if not f then error(error_message) end
-- besser:
assert(io.open(path, 'r'))
Variadische Funktionen
- Syntax
function foo(...) <body> end
- Hallo, Perl
function foo(...)
local a,b = ...
return a+b
end
- Hallo, Haskell
function map(f, x, ...)
if not x then return end
return f(x), map(f, ...)
end
Variadische Funktionen II
...
in Tabelle packen
function sum(...)
local s = 0
for _,v in ipairs{...} do s = s + (v or 0) end
return s
end
- Probleme mit
nil
s
print(sum(1,2,3,nil,4,5)) --> 6
select
to the rescue!
n = select('#', ...)
for i = 1,n do s = s + select(i, ...) or 0 end
Signal/Slots
reg = {}
function emit(s, ...)
for f in pairs(reg[s] or {}) do f(...) end
end
function register(s, f)
if not reg[s] then reg[s] = {} end
reg[s][f] = f
return f
end
function remove(s,f) (reg[s] or {})[f] = nil end
f = register('foo', function(a) print('f:', a) end)
g = register('foo', function(a) print('g:', a) end)
emit('foo', 'bar') --> f: bar; g: bar
Closures
- Funktionen haben Zugriff auf lokale Variablen des umschließenden Blocks (lexical scoping)
do
local foo = 'bar'
function f() print(foo) end
end
- Lexical scoping + first class functions
function counter()
local i = 0
return function() i = i + 1; return i end
end
c1,c2 = counter(), counter()
print(c1()) --> 1
print(c1(), c2()) --> 2 1
case
!
function case(x)
return function(t)
local c = t[x or 'default'] or t.default
return type(c) == "function" and c() or c
end
end
foo = 1
bar = case (foo) {
[1] = 'foo';
[2] = 'bar';
}
case (bar) {
bar = function() print('bar') end;
default = function() print('default') end
} --> default
Kapselung
- Closure-Klasse:
function Asteroid(x,y,dy,dx)
local radius = math.random(5,15)
local obj = {}
function obj.update(dt)
x, y = x + dx*dt, y + dy*dt
end
function obj.draw() drawCircle(x,y,radius) end
function obj.circle() return x,y,radius end
return obj
end
asteroid = Asteroid(100,100, 2,-4)
asteroid.update(0.5)
print(asteroid.circle()) -- 98 101 13
Funktionen überschreiben
- Prinzip
do
local _print = print
function print(...) _print('foo', ...) end
end
- Verhalten ändern
do
local _open = io.open
function io.open(...)
if open_ok(...) then return _open(...) end
return nil, "Access denied"
end
end
Iterators/Generators
- Closures as Generator:
function fib(n)
n = n or math.huge
local a,b = 1, 0
return function()
a, b, n = b, a+b, n-1
if n >= 0 then return b end
end
end
for i in fib(10) do print(i) end
Generisches for
- Syntax:
for v1, ..., vn in <explist> do <something> end
- Äquivalent:
do
local gen, state, val = <explist>
while true do
local v1, ..., vn = gen(state, val)
val = v1
if val == nil then break end
<something>
end
end
Zustandslose Generatoren
ipairs
local function iter(a, i)
i = i + 1
if a[i] then return i, a[i] end
end
function ipairs(t) return iter, t, 0 end
- Komplexer Zustand
local function iter(s, b)
s.a, b, s.n = b, s.a + b, s.n-1
if s.n >= 0 then return b end
end
function fib(n) return iter, {n=n, a=1}, 0 end
Coroutine Basics
co = coroutine.create(function()
for i = 1,10 do coroutine.yield(i) end
end)
print(coroutine.status(co)) --> suspended
repeat
local ok, i = coroutine.resume(co)
print(i) --> 1; 2; ...; 10; nil
until i == nil
print(coroutine.status(co)) --> dead
print(coroutine.resume(co))
--> false cannot resume dead coroutine
Abkürzung
f = coroutine.wrap(function()
for i = 1,10 do coroutine.yield(i) end
end)
repeat
local v = f()
print(v) --> 1; 2; ...; 10; nil
until v == nil
f() --> stdin:1: cannot resume dead coroutine
--> stack traceback:
--> [C]: in functionion 'f'
--> stdin:1: in main chunk
--> [C]: ?
Coroutine als Generator
function words(path)
return coroutine.wrap(function()
for line in io.lines(path) do
for w in line:gmatch('%w+') do
coroutine.yield(w)
end
end
end)
end
for w in words('index.html') do
print(w) --> DOCTYPE; html; html; head; meta; ...
end
Metatable Basics
- Angehängte Tabellen
t, mt = {a = 1}, {}
print(getmetatable(t)) --> nil
setmetatable(t, mt)
print(getmetatable(t) == mt) --> true
- Nur für
table
, function
und userdata
möglich
print(getmetatable("hi")) --> table: 0x1c9d4a0
print(getmetatable(0)) --> nil
- Schwarze Magie
debug.setmetatable(0, {__index = _G})
(42):print() --> 42
Metamethoden
- Spezielle Felder in Metatables verändern Verhalten
- Arithmetik
complex = {}
function complex.new(re, im)
return setmetatable({re,im}, complex)
end
function complex.__add(x,y)
return complex.new(x[1] + y[1], x[2] + y[2])
end
function complex.__sub(x,y)
return complex.new(x[1] - y[1], x[2] - y[2])
end
Arithmetik cont.
function complex.__mul(x,y)
local a,b, c,d = x[1],x[2], y[1],y[2]
return complex.new(a*c - b*d, a*d + b*c)
end
function complex.__div(x,y)
local a,b, c,d = x[1],x[2], y[1],y[2]
local re = (a*c + b*d)/(c*c + d*d)
local im = (b*c - a*d)/(c*c + d*d)
return complex.new(re, im)
end
a,b = complex.new(1,2), complex.new(3,4)
c,d,e = a+b, a*b, a/b
Relationen
function complex.__eq(x,y)
return x[1] == y[1] and x[2] == y[2]
end
function complex.__le(a,b)
return x[1] < y[1] and x[2] <= y[2]
end
function complex.__lt(a,b)
local a,b,c,d = x[1],x[2], y[1],y[2]
return a < c or (a == c and b <= d)
end
tostring()
& friend
function complex.__tostring(a)
return "(" .. a[1] .. " + " .. a[2] .. "i)"
end
function complex.__concat(a,b)
return tostring(a) .. tostring(b)
end
print(complex.new(2,3)) --> (2 + 3i)
s = "answer = " .. complex.new(4,2)
print(s) --> answer = (4 + 2i)
__index
und __newindex
function complex.__index(x, k)
return ({re = x[1], im = x[2]})[k]
end
function complex.__newindex(x, k, v)
local i = ({re = 1, im = 2})[k]
x[i] = v
end
c = complex.new(math.pi, -1)
print(c.re, c.im) --> 3.14159... -1
c.re, c.im = 3, 1
print(c.re, c.im) --> 3 1
Lazy Loading
images = setmetatable({}, {__index = function(_, p)
p = "images/" .. p .. ".png"
print("loading", p)
local img = assert(loadImage(p))
images[path] = img
return img
end})
draw(images.troll) --> loading images/troll.png
draw(images.troll) --> <nix>
Read-Only Tables
function readOnly(t)
return setmetatable(t, {__newindex = function(_,k)
error("Cannot create field " .. k)
end})
end
t = readOnly{
foo = 1,
bar = 2
}
t.foo = 2
t.baz = 3 --> stdin:1: Cannot create field baz
Dinge, für die keine Zeit mehr ist
__call
und __mode
- OOP Paradigmen
- Environments und
_G
loadstring()
und dofile()
require
und das Modulsystem
- Standard Libraries:
math
, table
, string
, io
, os
und debug
.
- Third Party:
luasocket
, lfs
, luaprofiler
, …
- C-API