Metatabellen
Jede Tabelle kann eine Metatabelle haben: eine zweite, gewöhnliche Tabelle mit Anweisungen für Lua, was in besonderen Situationen mit der ersten Tabelle zu tun ist. Du hängst eine mit setmetatable(tabelle, metatabelle) an.
Die Anweisungen stehen unter besonderen Schlüsseln, die mit zwei Unterstrichen beginnen. Der nützlichste ist __index. Er sagt Lua, wo es suchen soll, wenn du einen Schlüssel liest, den die Tabelle nicht hat:
local marktPreise = {karotte = 5, kartoffel = 8, kuerbis = 20}
local hugoPreise = {kuerbis = 25}
setmetatable(hugoPreise, {__index = marktPreise})
print(hugoPreise.kuerbis, hugoPreise.karotte, hugoPreise.mais)Das gibt Folgendes aus:
25 5 nilHugo, der Händler, zahlt die üblichen Marktpreise, nur für Kürbisse zahlt er mehr. Seine Tabelle enthält genau diesen einen Preis, und Lua sucht die anderen woanders:
hugoPreise.kuerbissteht in Hugos Tabelle, also ist es25.hugoPreise.karottesteht nicht darin. Lua sieht in der Metatabelle der Tabelle nach, findet dort__indexund liestkarotteaus der Tabelle, auf die__indexzeigt,marktPreise:5.maissteht in keiner der beiden Tabellen, das Ergebnis ist alsonil, wie bei jedem fehlenden Schlüssel.
__index wird nur benutzt, wenn du einen fehlenden Schlüssel liest. Wenn du einen Schlüssel schreibst, kommt er in die Tabelle selbst, und die andere Tabelle ändert sich nicht: Nach hugoPreise.karotte = 6 zahlt Hugo 6 für Karotten, und marktPreise.karotte ist immer noch 5.
Was eine Metatabelle unter so einem besonderen Schlüssel hält, heißt Metamethode. __index ist die erste, die du kennst, und in dieser Lektion lernst du noch mehr kennen. getmetatable(t) gibt die Metatabelle von t zurück, oder nil, wenn es keine gibt.
Gemeinsame Methoden. So können Objekte ihre Methoden teilen. Lege die Methoden in eine Tabelle und gib jedem Objekt eine Metatabelle, deren __index diese Tabelle ist:
local pflanzenMethoden = {}
function pflanzenMethoden:wachsen()
self.alter = self.alter + 1
end
function pflanzenMethoden:beschreiben()
print(self.name .. ": Tag " .. self.alter .. " von " .. self.tage)
end
local function neuePflanze(name, tage)
local pflanze = {name = name, tage = tage, alter = 0}
setmetatable(pflanze, {__index = pflanzenMethoden})
return pflanze
end
local karotte = neuePflanze("Karotte", 3)
local kuerbis = neuePflanze("Kürbis", 7)
karotte:wachsen()
karotte:beschreiben()
kuerbis:beschreiben()
print(karotte.wachsen == kuerbis.wachsen)Das gibt Folgendes aus:
Karotte: Tag 1 von 3
Kürbis: Tag 0 von 7
trueDas passiert bei karotte:wachsen():
- Es ist die Kurzform von
karotte.wachsen(karotte), also liest Lua zuerstkarotte.wachsen. Die Karotte enthält nurname,tageundalter; einwachsenist nicht darin. - Die Metatabelle der Karotte hat
__index, also sieht Lua inpflanzenMethodennach und findet dortwachsen. - Lua ruft diese Funktion mit der Karotte als
selfauf, sodassself.alter = self.alter + 1die Karotte verändert, nichtpflanzenMethoden.
Jede Pflanze enthält jetzt nur ihre eigenen Daten, und jede Methode gibt es nur einmal: karotte.wachsen == kuerbis.wachsen ist true. Lua sucht eine Methode in dem Moment, in dem du sie aufrufst, deshalb funktioniert eine Methode, die du später zu pflanzenMethoden hinzufügst, auch für Pflanzen, die es schon gibt.
Das Geheimnis von s:upper(). Auch Texte haben eine Metatabelle, eine, die alle Texte teilen, und ihr __index ist die Tabelle string:
print(getmetatable("karotte").__index == string)Das gibt Folgendes aus:
trueAlso ist s:upper() gleich s.upper(s). Ein Text hat keine eigenen Schlüssel, deshalb sieht Lua in seinem __index nach, und das ist string, und findet string.upper. Es ist derselbe Mechanismus wie bei den Pflanzen.
So schreibst du es:
Anweisungen
- 1
Lass die Hühner ihre Methoden teilen. Erzeuge über
neuesHuhneine TabellehuhnMethodenund verschiebe beide Methoden hinein (function huhnMethoden:legen(anzahl), …). Gib inneuesHuhnjedem neuen Huhn eine Metatabelle, deren__indexhuhnMethodenist. - 2
Führe das Programm aus: Die letzte Zeile gibt jetzt
trueaus, weil beide Hühner dieselbe Funktion benutzen. Füge noch eine Methode hinzu,huhnMethoden:einsammeln(), die die Eier des Huhns zurückgibt und auf 0 zurücksetzt. Du kannst sie unter der Zeile schreiben, dietrueausgibt: Die Hühner suchen ihre Methoden erst, wenn du sie aufrufst. Sammle am Ende des Programms die Eier beider Hühner ein und gib aus, wie viele es zusammen sind:Goldi hat 2 Eier gelegt Perle hat 4 Eier gelegt true Eingesammelt: 6 Eier