Metatabulky
Každá tabulka může mít metatabulku: druhou, obyčejnou tabulku s pokyny, co má Lua s první tabulkou dělat ve zvláštních situacích. Připojíte ji funkcí setmetatable(tabulka, metatabulka).
Pokyny jsou uložené pod zvláštními klíči, které začínají dvěma podtržítky. Nejužitečnější z nich je __index. Říká, kde má Lua hledat, když čtete klíč, který tabulka nemá:
local trzniCeny = {mrkev = 5, brambory = 8, dyne = 20}
local cenyHugo = {dyne = 25}
setmetatable(cenyHugo, {__index = trzniCeny})
print(cenyHugo.dyne, cenyHugo.mrkev, cenyHugo.kukurice)Vypíše:
25 5 nilObchodník Hugo platí běžné tržní ceny, jen za dýně dává víc. Jeho tabulka proto obsahuje jedinou cenu a ostatní hledá Lua jinde:
cenyHugo.dynev Hugově tabulce je, takže výsledek je25.cenyHugo.mrkevtam není. Lua se podívá do metatabulky, najde v ní__indexa přečtemrkevz tabulky, na kterou__indexodkazuje, tedy ztrzniCeny:5.kukuricenení ani v jedné tabulce, takže výsledek jenil, jako u každého chybějícího klíče.
__index se použije jen při čtení chybějícího klíče. Když klíč zapisujete, uloží se do tabulky samotné a druhá tabulka se nezmění: po cenyHugo.mrkev = 6 platí Hugo za mrkev 6, ale trzniCeny.mrkev je pořád 5.
Tomu, co metatabulka drží pod takovým zvláštním klíčem, se říká metametoda. __index je první, kterou znáte, a v této lekci poznáte další. Funkce getmetatable(t) vrátí metatabulku tabulky t, nebo nil, když žádnou nemá.
Společné metody. Přesně takhle můžou objekty sdílet metody. Dejte metody do jedné tabulky a každému objektu dejte metatabulku, jejíž __index je právě tahle tabulka:
local metodyPlodiny = {}
function metodyPlodiny:dalsiDen()
self.vek = self.vek + 1
end
function metodyPlodiny:popis()
print(self.nazev .. ": den " .. self.vek .. "/" .. self.dny)
end
local function novaPlodina(nazev, dny)
local plodina = {nazev = nazev, dny = dny, vek = 0}
setmetatable(plodina, {__index = metodyPlodiny})
return plodina
end
local mrkev = novaPlodina("Mrkev", 3)
local dyne = novaPlodina("Dýně", 7)
mrkev:dalsiDen()
mrkev:popis()
dyne:popis()
print(mrkev.dalsiDen == dyne.dalsiDen)Vypíše:
Mrkev: den 1/3
Dýně: den 0/7
trueCo se stane při mrkev:dalsiDen():
- Je to zkratka pro
mrkev.dalsiDen(mrkev), takže Lua nejdřív čtemrkev.dalsiDen. V mrkvi jsou jennazev,dnyavek, žádnédalsiDen. - Metatabulka mrkve má
__index, a tak se Lua podívá dometodyPlodinyadalsiDentam najde. - Lua tu funkci zavolá s mrkví jako
self, takžeself.vek = self.vek + 1změní mrkev, a nemetodyPlodiny.
Každá plodina teď drží jen svá data a každá metoda existuje jen jednou: mrkev.dalsiDen == dyne.dalsiDen je true. Lua metodu hledá až ve chvíli, kdy ji voláte, takže metoda, kterou do metodyPlodiny přidáte později, funguje i pro plodiny, které už existují.
Tajemství s:upper(). I texty mají metatabulku, jednu společnou pro všechny texty, a její __index je tabulka string:
print(getmetatable("mrkev").__index == string)Vypíše:
trues:upper() je tedy s.upper(s). Text žádné vlastní klíče nemá, a tak se Lua podívá do jeho __index, tedy do tabulky string, a najde string.upper. Je to stejný mechanismus jako u plodin.
Jak psát:
Pokyny
Ať slepice sdílejí metody. Nad funkcí novaSlepice vytvořte tabulku metodySlepice a přesuňte do ní obě metody (function metodySlepice:snes(pocet), …). V novaSlepice dejte každé nové slepici metatabulku, jejíž __index je metodySlepice.
Spusťte program: poslední řádek teď vypíše true, protože obě slepice používají stejnou funkci. Přidejte ještě metodu metodySlepice:seber(), která vrátí počet vajec slepice a nastaví ho zpátky na 0. Klidně ji napište až pod řádek, který vypisuje true: slepice hledají metody až ve chvíli, kdy je voláte. Na konci programu seberte vejce obou slepic a vypište, kolik jich je dohromady:
Zlatka snesla 2 vejce
Perla snesla 4 vejce
true
Sebráno: 6 vajecZačněte programovat zdarma
4/9