Metatables ermöglichen es Tabellen, mehr Macht als zuvor zu haben. Sie werden an Daten angehängt und enthalten Werte namens Metamethode. Metamethode werden ausgeführt, wenn eine bestimmte Aktion mit dem Datum verwendet wird, zu dem es angehängt ist.
Betrachten Sie den folgenden Codes:
local list = {1, 2}print(list[3])
Du könntest dich erwarten, dass dieser Code durch die Liste für den dritten Index in der Liste gesucht wird, findet nichts und gibt null zurück. Das ist jedoch nicht richtig. Was tatsächlich passiert, ist, dass der Code durch die Liste für den dritten Index gesucht wird, findet nichts und dann prüft, ob ein Metatable an der Tabelle angehängt ist, und gibt nil zurück, wenn keines vorhanden ist.
Metatables verwalten
Die beiden primären Funktionen für das Hinzufügen und Finden einer Tabelle's Metatable sind setmetatable und getmetatable
local x = {}local metaTable = {} -- metaTables sind Tables, auch!setmetatable(x, metaTable) -- Geben Sie x eine Metatabel namens metaTable!print(getmetatable(x)) --> table: [hexadecimal memory address]
Die Funktion setmetatable gibt auch die Tabelle zurück, auf die Sie das Metatable einstellen, so dass diese beiden Skripte dasselbe tun:
local x = {}setmetatable(x, {})
local x = setmetatable({}, {})
Metam Methods
Metamethods sind die Funktionen, die in einer Metatabelle gespeichert sind. Sie können von der Aufrufung eines Tables, über das Hinzufügen eines Tables, bis hin zum Teilen von Tables, um nur einige zu nennen. Hier ist die Liste der verfügbaren Metam Methods:
Methode | Beschreibung |
---|---|
__index(tabelle, index) | Feuert, wenn die Tabelle [Index] indiziert ist, wenn die Tabelle [Index] null ist. Kann auch auf eine Tabelle festgelegt werden, in dem Fall wird diese Tabelle indiziert. |
__newindex(tabelle, index, wert) | Feuert, wenn sich die Tabelle [Index] versucht, festgelegt zu werden (Tabelle [Index] = Wert), wenn die Tabelle null ist. Sie können auch auf eine Tabelle festgelegt werden, in der diese Tabelle dann indiziert wird. |
__call(tabelle, ...) | Feuert, wenn die Tabelle wie eine Funktion aufgerufen wird, ... sind die Argumente, die übergeben wurden. |
__concat(tabelle, wert) | Feuert, wenn der .. Konjunktionsbetreiber auf dem Tisch verwendet wird. |
__unm(tabelle) | Feuert, wenn der unary -Betreiber auf dem Tisch verwendet wird. |
__add(tabelle, wert) | Der + -Betreiber. |
__sub(tabelle, wert) | Der - Subtraktionsbetreiber. |
__mul(tabelle, wert) | Der * Multiplikationsbetreiber. |
__div(tabelle, wert) | Der / Division Operator. |
__idiv(tabelle, wert) | Der // Floor Division Operator. |
__mod(tabelle, wert) | Der %-Modus-Betreiber. |
__pow(tabelle, wert) | Der ^ exponentielle Operator. |
__toststring(tabelle) | Fired, wenn tostring auf der Tabelle aufgerufen wird. |
__metabel | Wenn vorhanden, sperrt die Metable so, dass getmetable dies stattdessen als Metable und setmetable zurückgibt, und setmetable wird stattdessen auf einen Fehler fest. Nicht-Funktionswert. |
__eq(tabelle, wert) | Die == gleich Operator¹ |
__lt(tabelle, wert) | Der < weniger als Operator¹ |
__le(tabelle, wert) | Der Operator << |
__modus | Wird in schwachen Tabellen verwendet, um zu erklären, ob die Schlüssel und/oder Werte eines Tabellen schwach sind. Beachten Sie: Verweise auf Roblox-Instanzen sind niemals schwach. Tabellen, die solche Verweise enthalten, werden niemals gesammelt. |
__gc(tabelle) | Feuere, wenn die Tabelle Müll gesammelt wird. Hinweis: Auf Roblox ist diese Metamethode deaktiviert. |
__len(tabelle) | Feuert, wenn die # Länge Operator auf dem Objekt verwendet wird. |
__iter(tabelle) | Wird verwendet, um einen benutzerdefinierten Iterator zu deklarieren, wenn ein generisches Iterator verwendet wird. |
Es sollte beachtet werden, dass wenn Sie Funktionen für arithmetrische oder relationale Metam Methods schreiben, die beiden Funktions参数 sind zwischen der Tabelle, die das Metam Methods ausgeführt hat, und dem anderen Werte. Zum Beispiel, wenn Sie Vector Operationen mit Skalarem Division ist nicht kombinativ. Deshalb, wenn Sie Metam Methods für Ihren eigenen Vector2 Class schreiben, Sie würden vorsichtig sein, um für jedes Szenario zu berücksichtigen.
local vector2 = {__type = "vector2"}
local mt = {__index = vector2}
function mt.__div(a, b)
if type(a) == "number" then
-- a ist ein skalar, b ist ein vektorkraft
local scalar, vector = a, b
return vector2.new(scalar / vector.x, scalar / vector.y)
elseif type(b) == "number" then
-- a ist ein vektorkraft, b ist ein skalar
local vector, scalar = a, b
return vector2.new(vector.x / scalar, vector.y / scalar)
elseif (a.__type and a.__type == "vector2" and b.__type and b.__type == "vector2") then
-- beide a und b sind vector
return vector2.new(a.x / b.x, a.y / b.y)
end
end
function mt.__tostring(t)
return t.x .. ", " .. t.y
end
function vector2.new(x, y)
local self = setmetatable({}, mt)
self.x = x or 0
self.y = y or 0
return self
end
local a = vector2.new(10, 5)
local b = vector2.new(-3, 4)
print(a / b) -- –3.333333333333, 1.25
print(b / a) -- – 0,3, 0,8
print(2 / a) -- 0.2, 0.4
print(a / 2) -- 5, 2.5
Verwendung von Metatables
Es gibt viele Möglichkeiten, Metatables zu verwenden, z. B. die __unm Metamethode (um eine Tabelle negativ zu machen):
local metatable = {
__unm = function(t) -- __unm ist für den einzelbetreiber
local negated = {}
for key, value in t do
negated[key] = -value -- alle werte in dieser tabelle negieren
end
return negated -- gib die tabelle zurück
end
}
local table1 = setmetatable({10, 11, 12}, metatable)
print(table.concat(-table1, "; ")) --> -10; -11; -12
Hier ist ein interessanter Weg, Dinge mit __index zu deklarieren:
local metatable = {__index = {x = 1}}local t = setmetatable({}, metatable)print(t.x) --> 1
__index wurde ausgeführt, als x in der Tabelle indexiert wurde und nicht gefunden wurde. Lua suchte dann durch die __index-Tabelle nach einem Index namens x und kehrte dann zurück, indem er einen gefunden hatte.
Jetzt kannst du das mit einer einfachen Funktion tun, aber es gibt noch viel mehr, wo das herkommt. Nimm zum Beispiel dies:
local t = {10, 20, 30}print(t(5))
Nun, offensichtlich kannst du keinen Tabelle aufrufen. Das ist einfach verrückt, aber (überraschung, überraschung!) mit Metatabellen kannst du.
local metatable = {
__call = function(t, param)
local sum = {}
for i, value in ipairs(t) do
sum[i] = value + param -- Fügen Sie dem Wert (5) das Argument hinzu und platzieren Sie es dann in der neuen Tabelle (t).
end
return unpack(sum) -- Geben Sie die einzelnen Tabellenwerte zurück
end
}
local t = setmetatable({10, 20, 30}, metatable)
print(t(5)) --> 15 25 35
Du kannst noch viel mehr tun, wie zusätzliche Tabellen hinzufügen!
local table1 = {10, 11, 12}local table2 = {13, 14, 15}for k, v in table1 + table2 doprint(k, v)end
Dies wird mit der Fehlmeldung zurückgegeben, dass Sie versuchen, Arithmetik auf einem Table auszuführen. Versuchen Sie dies mit einem Metatable.
local metatable = {
__add = function(t1, t2)
local sum = {}
for key, value in t1 do
sum[key] = value
end
for key, value in t2 do
if sum[key] then
sum[key] += value
else
sum[key] = value
end
end
return sum
end
}
local table1 = setmetatable({10, 11, 12}, metatable)
local table2 = setmetatable({13, 14, 15}, metatable)
for k, v in table1 + table2 do
print(k, v)
end
Verwenden Sie Fälle
Nun, all diese Beispiele können als einfache Funktion implementiert werden, aber Sie können viel mehr als das tun. Lassen Sie uns ein einfaches Programm versuchen, das eine Anzahl speichert, wenn ein möglicherweise laggy Mathe-Problem in es eingelegt wird.
Für diesen werden wir die __index Metamethode verwenden, um es einfach zu machen:
local function mathProblem(num)
for i = 1, 20 do
num = math.floor(num * 10 + 65)
end
for i = 1, 10 do
num += i - 1
end
return num
end
local metatable = {
__index = function(object, key)
local num = mathProblem(key)
object[key] = num
return num
end
}
local t = setmetatable({}, metatable)
print(t[1]) -- Wird langsam, da dies die erste Verwendung dieser Zahl ist, und muss die Mathe-Funktion ausführen.
print(t[2]) -- wird langsam, da dies die erste verwendung dieser zahl ist.
print(t[1]) -- will be fast because it's just grabbing the number from the table.
Rawset, Rawget, Rawequal
Wenn Sie mit Metatables spielen, können Sie einige Probleme bemerken. Was passiert, wenn Sie die __index Metamethode verwenden müssen, um neue Werte in einer Tabelle zu erstellen, aber das Metatabelle dieses Tables auch ein __newindex Metamethode in sich hat? Sie möchten die Lua-eingebaute Funktion rawset verw
local t = setmetatable({}, {
__index = function(self, i)
self[i] = i * 10 -- nur als beispiel
return self[i]
end,
__newindex = function(self, i, v)
--tun sie nichts, weil wir nicht möchten, dass sie werte auf die tabelle auf die übliche weise einstellen
end
})
print(t[1]) -- Causes a C-Stack overflow
Jetzt, warum verursacht das einen Stack-Überlauf? Stack-Überläufe passieren, wenn Sie versuchen, eine Funktion von sich selbst zu viele Male aufrufen, aber was verursacht, dass dies geschieht? In der __index-Funktion setzen wir self[i] auf einen Wert, sodass wenn es zur nächsten Zeile kommt,
Das Problem ist, dass __newindex uns nicht erlaubt, den Wert zu setzen. Seine Präsenz stoppt Werte, die der Tabelle mit der Standardmethode t[i] = v hinzugefügt werden. Um dies zu überwinden, verwenden Sie die Funktion rawset.
local t = setmetatable({}, {
__index = function(self, i)
rawset(self, i, i * 10)
return self[i]
end,
__newindex = function(self, i, v)
--tun sie nichts, weil wir nicht möchten, dass sie werte auf die tabelle auf die übliche weise einstellen
end
})
print(t[1]) -- prints 10
Verwendung des Set-Datentyps
Ein Set ist eine Sammlung von Elementen ohne Ordnung und ohne doppelte Elemente. Ein Element ist entweder oder nicht in einem festlegenenthalten. Mit der Verwendung von Metatabellen können Sie Elemente innerhalb von Lua-Skripts aufgebaut und manipuliert werden.
Grundlegende Methoden
Der folgende Code enthält grundlegende Set-Funktionen, mit denen Sie neue Sätze erstellen, ein Artikelhinzufügen und entfernen und die Inhalte eines Artikelausgeben.
local Set = {}
Set.__index = Set
-- Funktion zum Erstellen einer Reihe aus einer optionalen Liste von Elementen
function Set.new(items)
local newSet = {}
for key, value in items or {} do
newSet[value] = true
end
return setmetatable(newSet, Set)
end
-- Funktion, um ein Element zu einem festlegenhinzuzufügen
function Set:add(item)
self[item] = true
end
-- Funktion, um einen Element aus einer festlegenzu entfernen
function Set:remove(item)
self[item] = nil
end
-- Funktion zum Überprüfen, ob ein Set ein Artikelenthält
function Set:contains(item)
return self[item] == true
end
-- Funktion, um die Ausgabe als komma-delimited-Liste für dasDebuggen zu exportieren
function Set:output()
local elems = {}
for key, value in self do
table.insert(elems, tostring(key))
end
print(table.concat(elems, ", "))
end
Set erstellen
Ein neues Set kann aufgebaut werden, indem Set.new() mit einer optionalen Reihe von Elementen aufgerufen wird, um hinzufügen.
local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})
Beachten Sie, dass definiert eine Reihe kein Konzept der Ordnung.
Item hinzufügen
Ein Item zu einem bestehenden Set hinzuzufügen, kann mit der Methode Set:add() gemacht werden.
local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})fruits:add("Mango")
Item entfernen
Um ein Element aus einer festlegenzu entfernen, rufe Set:remove() mit dem Item-Namen auf.
local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})fruits:remove("Orange")
nach Gegenstand suchen
Um zu überprüfen, ob ein Set ein bestimmtes Artikelenthält, verwenden Sie Set:contains() .
local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})local result1 = fruits:contains("Cherry")print(result1) -- wahrlocal result2 = fruits:contains("Watermelon")print(result2) -- false
Zusätzliche Methoden
Andere nützliche Operationen können für Sätze implementiert werden, sodass Sie Elemente zwischen Sätzen vergleichen, Sätze kombinieren oder ein Satz von einem anderen Satz abziehen können.
Kreuzung
Wenn Sie Sätze als Venn-Diagramme betrachten, können Sie das Erfüllungsgleichnis von zwei Sätzen wie folgt erhalten, was die Elemente bedeutet, die in beiden Sätzen erscheinen.
local function getIntersection(set1, set2)
local result = Set.new()
for key, value in set1 do
if set2:contains(key) then
result:add(key)
end
end
return result
end
local freshFruits = Set.new({"Mango", "Lemon", "Orange", "Cherry", "Lime", "Peach"})
local frozenFruits = Set.new({"Mango", "Peach", "Pineapple"})
local commonFruits = getIntersection(freshFruits, frozenFruits)
commonFruits:output() -- Mango, Peach
Union
Du kannst die Union von zwei Sätzen mit der folgenden Funktion erhalten, was eine Sammlung der Elemente in beiden Sätzen mit keinen Duplikaten bedeutet. Beachte, dass diese Funktion die metatable __add -Methode verwendet, um einen Zusatz-Shortcut von __add zu liefern.
function Set:__add(otherSet)
local result = Set.new()
for entry in self do
result[entry] = true
end
for entry in otherSet do
result[entry] = true
end
return result
end
local sweetFruits = Set.new({"Apple", "Mango", "Cherry", "Peach"})
local sourFruits = Set.new({"Lemon", "Lime"})
local allFruits = sweetFruits + sourFruits
allFruits:output() -- Pfirsich, Lime, Apfel, Kirsche, Zitrone, Mango
Subtraktion
Sie können alle Elemente in einer Reihe von den Elementen in einer anderen Reihe via die folgende Funktion entfernen. Ähnlich wie die Funktion oben verwendet diese die Metatable __sub -Methode, um einen Subtraktions-Shortcut von set1 - set2 zu bieten.
function Set:__sub(otherSet)
local result = Set.new()
for entry in self do
result[entry] = true
end
for entry in otherSet do
result[entry] = nil
end
return result
end
local allFruits = Set.new({"Apple", "Lemon", "Mango", "Cherry", "Lime", "Peach"})
local sourFruits = Set.new({"Lemon", "Lime"})
local sweetFruits = allFruits - sourFruits
sweetFruits:output() -- Mango, Apple, Cherry, Peach