Kurz: Eine Metatabelle definiert, wie sich eine Tabelle bei bestimmten Operationen verhält (Rechnen, Vergleichen, Ausgeben, fehlende Schlüssel). Du setzt sie mit setmetatable(t, mt).
Teil des Kurses Lua
Metatabellen
Eine Metatabelle definiert, wie sich eine Tabelle bei bestimmten Operationen verhält (Rechnen, Vergleichen, Ausgeben, fehlende Schlüssel). Du setzt sie mit setmetatable(t, mt).
local Vektor = {}
Vektor.__index = Vektor
function Vektor.neu(x, y) return setmetatable({x = x, y = y}, Vektor) end
function Vektor.laenge(v) return math.sqrt(v.x ^ 2 + v.y ^ 2) end
Vektor.__add = function(a, b) return Vektor.neu(a.x + b.x, a.y + b.y) end
Vektor.__mul = function(a, k) return Vektor.neu(a.x * k, a.y * k) end
Vektor.__eq = function(a, b) return a.x == b.x and a.y == b.y end
Vektor.__lt = function(a, b) return a:laenge() < b:laenge() end
Vektor.__tostring = function(v) return "(" .. v.x .. ", " .. v.y .. ")" end
Vektor.__len = function(v) return 2 end
Vektor.__call = function(v, faktor) return v * faktor end
Vektor.__unm = function(v) return Vektor.neu(-v.x, -v.y) end
Vektor.__concat = function(a, b) return tostring(a) .. tostring(b) end
local a, b = Vektor.neu(1, 2), Vektor.neu(3, 4)
print(tostring(a + b), tostring(a * 3), tostring(-a), a == Vektor.neu(1, 2), a < b, #a, tostring(a(10)))
print(a:laenge(), b:laenge(), a .. b)Ausgabe:
(4, 6) (3, 6) (-1, -2) true true 2 (10, 20)
2.2360679774998 5.0 (1, 2)(3, 4)| Metamethode | Wann |
|---|---|
__index | Lesen eines fehlenden Schlüssels (Tabelle oder Funktion) |
__newindex | Schreiben eines fehlenden Schlüssels |
__add __sub __mul __div __mod __pow __unm __idiv | Rechenoperatoren |
__eq __lt __le | Vergleiche |
__concat __len __call | .., #, Aufruf wie Funktion |
__tostring | tostring() / print |
__close, __gc | Aufräumen |
Standardwerte und schreibgeschützte Tabellen
local standard = setmetatable({}, {__index = function() return 0 end})
standard.a = 5
print(standard.a, standard.b) -- fehlender Schlüssel liefert 0
local zaehler = setmetatable({}, {__index = function() return 0 end})
for _, w in ipairs({"a", "b", "a"}) do zaehler[w] = zaehler[w] + 1 end
print(zaehler.a, zaehler.b, zaehler.c)
local function nur_lesen(t)
return setmetatable({}, {
__index = t,
__newindex = function(_, k) error("Tabelle ist schreibgeschützt: " .. tostring(k), 2) end,
__len = function() return #t end,
})
end
local konfig = nur_lesen({port = 8080, host = "localhost"})
print(konfig.port, konfig.host)
print(pcall(function() konfig.port = 1 end))Ausgabe:
5 0
2 1 0
8080 localhost
false main.lua:18: Tabelle ist schreibgeschützt: portKlassen und Vererbung
Lua hat keine Klassen, aber mit Tabellen und __index baut man sie in wenigen Zeilen:
local Tier = {}
Tier.__index = Tier
function Tier.neu(name)
local self = setmetatable({}, Tier)
self.name = name
return self
end
function Tier:laut() return "..." end
function Tier:vorstellen() return self.name .. " sagt " .. self:laut() end
local Hund = setmetatable({}, {__index = Tier}) -- Hund erbt von Tier
Hund.__index = Hund
function Hund.neu(name)
local self = Tier.neu(name)
return setmetatable(self, Hund)
end
function Hund:laut() return "Wuff" end
function Hund:apportiere(was) return self.name .. " holt " .. was end
local Katze = setmetatable({}, {__index = Tier})
Katze.__index = Katze
function Katze.neu(name, leben)
local self = setmetatable(Tier.neu(name), Katze)
self.leben = leben or 9
return self
end
function Katze:laut() return "Miau" end
function Katze:vorstellen() return Tier.vorstellen(self) .. " (" .. self.leben .. " Leben)" end
local tiere = {Hund.neu("Rex"), Katze.neu("Mimi")}
for _, t in ipairs(tiere) do print(t:vorstellen()) end
print(tiere[1]:apportiere("Stock"))
print(getmetatable(tiere[1]) == Hund, tiere[2].apportiere)Ausgabe:
Rex sagt Wuff
Mimi sagt Miau (9 Leben)
Rex holt Stock
true nilDer Doppelpunkt obj:methode(a) ist Kurzschreibweise für obj.methode(obj, a); bei der Definition function Klasse:methode() entsteht automatisch der Parameter self.
Eine kleine Klassenhilfe
local function klasse(eltern)
local c = {}
c.__index = c
if eltern then setmetatable(c, {__index = eltern}) end
c.neu = function(...)
local o = setmetatable({}, c)
if o.init then o:init(...) end
return o
end
return c
end
local Konto = klasse()
function Konto:init(inhaber, stand) self.inhaber, self.stand = inhaber, stand or 0 end
function Konto:einzahlen(b)
assert(b > 0, "Betrag muss positiv sein")
self.stand = self.stand + b
return self
end
Konto.__tostring = function(k) return string.format("%s: %.2f", k.inhaber, k.stand) end
local Sparkonto = klasse(Konto)
Sparkonto.__tostring = Konto.__tostring
function Sparkonto:init(inhaber, stand, zins)
Konto.init(self, inhaber, stand)
self.zins = zins
end
function Sparkonto:zinsen() self.stand = self.stand * (1 + self.zins); return self end
local k = Konto.neu("Mia", 100):einzahlen(50)
local s = Sparkonto.neu("Tom", 1000, 0.05):zinsen()
print(tostring(k), tostring(s))
print(pcall(k.einzahlen, k, -5))Ausgabe:
Mia: 150.00 Tom: 1050.00
false main.lua:16: Betrag muss positiv seinMerke
- Metatabellen steuern Operatoren, fehlende Schlüssel (
__index,__newindex) und Ausgabe (__tostring) - Klassen = Tabelle +
__index; Vererbung = Metatabelle der Klasse zeigt auf die Oberklasse obj:methode()übergibtselfautomatisch- Für schreibgeschützte Tabellen und Standardwerte sind Metatabellen ideal
Übungsaufgabe
Baue eine Klasse Stapel mit push, pop, groesse und __tostring.
Quiz zur Selbstkontrolle
Wofür dient __index?
- Für das Lesen fehlender Schlüssel (Vererbung, Standardwerte) (richtig)
- Für den Index in Listen
- Für Sortierung
- Für Zahlen
Was bedeutet obj:methode()?
- obj.methode(obj): self wird automatisch übergeben (richtig)
- Eine Klassenmethode
- Ein Fehler
- Eine statische Methode
Wie definiert man Operatorüberladung für +?
- Metamethode __add (richtig)
- operator+
- __plus
- def add
Weiter im Kurs
Zurück: String-Funktionen und Muster
Weiter: Fehler, Module und Coroutinen
Alle Kapitel: Lua im Überblick