Metatabellen ermöglichen es Tabellen, stärker zu werden als zuvor.Sie werden an Daten angehängt und enthalten Werte namens Metamethoden.Metamethoden werden abgefeuert, wenn eine bestimmte Aktion mit dem Datum verwendet wird, zu dem es angehängt ist.
Metatabellen manipulieren
Die beiden primären Funktionen für das Hinzufügen und Finden eines metatabellenfähigen Tisches sind setmetatable() und getmetatable().
local x = {}local metaTable = {} -- metaTabellen sind auch Tabellen!setmetatable(x, metaTable) -- Gib x eine metatable namens metaTable!print(getmetatable(x)) --> table: [hexadecimal memory address]
Die setmetatable()-Funktion gibt auch die Tabelle zurück, für die Sie den metatable festlegen, also tun diese beiden Skripte dasselbe:
local x = {}setmetatable(x, {})
local x = setmetatable({}, {})
Metamethoden
Metamethoden sind die Funktionen, die in einem Metatable gespeichert werden.Sie können von der Anrufung eines Tisches bis zur Hinzufügung eines Tisches gehen, bis hin zur Teilung von Tabellen.Hier ist die Liste der verfügbaren Metamethoden:
Methode | Beschreibung |
---|---|
__index(table, index) | Feuert, wenn indexiert wird, wenn ist . Kann auch auf eine Tabelle festgelegt werden, in der Fall, dass diese Tabelle indexiert wird. |
__newindex(table, index, value) | Feuert, wenn versucht wird, festgelegt zu werden, wenn ist.Kann auch auf einen Tisch festgelegt werden, in dem Fall wird dieser Tisch indiziert. |
__call(table, ...) | Feuert, wenn der Tisch wie eine Funktion aufgerufen wird, ... sind die Argumente, die übergeben wurden. |
__concat(table, value) | Feuert, wenn der .. Konkatenz-Operator auf der Tabelle verwendet wird. |
__unm(table) | Feuert, wenn der binäre – -Operator auf der Tabelle verwendet wird. |
__add(table, value) | Der + Zusatzoperator. |
__sub(table, value) | Der – Subtraktionsoperator. |
__mul(table, value) | Der * Multiplikationsoperator. |
__div(table, value) | Der / Division-Operator. |
__idiv(table, value) | Der // Bodenteilungsoperator. |
__mod(table, value) | Der % Modulbetreiber. |
__pow(table, value) | Der ^ Exponentialrechnungsoperator. |
__tostring(table) | Feuert ab, wenn tostring auf dem Tisch aufgerufen wird. |
__metatable | Wenn vorhanden, sperrt das metatable, so dass getmetatable() dies anstelle des metatables zurückgibt und setmetatable() Fehler macht. Nicht funktionsfähiger Wert. |
__eq(table, value) | Die == gleich dem Operator¹ |
__lt(table, value) | Der < Weniger-Operator¹ |
__le(table, value) | Der <= Operator¹ |
__mode | Wird in schwachen Tabellen verwendet, um zu deklarieren, ob die Schlüssel und/oder Werte eines Tables schwach sind.Beachten Sie, dass sich die Referenzen auf Roblox-Instanzen niemals schwach sind.Tabellen, die solche Referenzen enthalten, werden niemals gesammelt. |
__len(table) | Feuert, wenn der Längenbetreiber # auf dem Objekt verwendet wird. |
__iter(table) | Wird verwendet, um einen benutzerdefinierten Iterator anzugeben, wenn generische Iteration verwendet wird. |
Es sollte beachtet werden, dass beim Schreiben von Funktionen für arithmetische oder relationale Metamethoden die beiden Funktions参数 zwischen der Tabelle, die die Metamethode abgefeuert hat, und dem anderen Wert austauschbar sind.Zum Beispiel, wenn beim Ausführen von Vektoperationsoperationen Division mit Skalaren nicht kommutativ ist.Daher, wenn du Metamethoden für deine eigene vector2-Klasse schreibst, möchtest du sicherstellen, dass du jedes Szenario berücksichtigst.
local vector2 = {__type = "vector2"}
local mt = {__index = vector2}
function mt.__div(a, b)
if type(a) == "number" then
-- a ist eine 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 eine 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
-- sowohl a als auch b sind vektoren
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.3333333333333, 1.25
print(b / a) -- -0.3, 0.8
print(2 / a) -- 0.2, 0.4
print(a / 2) -- 5, 2.5
Verwendung
Es gibt viele Möglichkeiten, Metatabellen zu verwenden, zum Beispiel die __unm Metamethode, um eine Tabelle negativ zu machen:
local metatable = {
__unm = function(t) -- __unm ist für den einfachen -operator
local negated = {}
for key, value in t do
negated[key] = -value -- alle werte in dieser tabelle verneinen
end
return negated -- tabelle zurückgeben
end
}
local table1 = setmetatable({10, 11, 12}, metatable)
print(table.concat(-table1, "; ")) --> -10; -11; -12
Hier ist eine interessante Art, Dinge mit __index zu deklarieren:
local metatable = {__index = {x = 1}}local t = setmetatable({}, metatable)print(t.x) --> 1
__index wurde abgefeuert, als x in der Tabelle indexiert und nicht gefunden wurde.Luau durchsuchte dann die __index Tabelle nach einem Index mit dem Namen x, und fand einen, und gab ihn zurück.
Jetzt kannst du das einfach mit einer einfachen Funktion tun, aber es gibt viel mehr, woher das kam. Nimm das zum Beispiel:
local t = {10, 20, 30}print(t(5))
In der Regel kannst du keine Tabelle aufrufen, aber mit Metatabellen kannst du:
local metatable = {
__call = function(t, param)
local sum = {}
for i, value in ipairs(t) do
sum[i] = value + param -- Füge das Argument (5) zum Wert hinzu und platziere es dann in der neuen Tabelle (t).
end
return unpack(sum) -- Gibt die einzelnen Tabellwerte zurück
end
}
local t = setmetatable({10, 20, 30}, metatable)
print(t(5)) --> 15 25 35
Du kannst auch viel mehr tun, zum Beispiel 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 einen Fehler auslösen, der besagt, dass du versuchst, auf einer Tabelle arithmetische Operationen durchzuführen, aber es funktioniert, wenn es mit einem Metatabelle versucht wird:
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
Wenn du mit Metatabellen spielst, kannst du auf einige Probleme stoßen.Wenn Sie die __index Metamethode verwenden müssen, um neue Werte in einer Tabelle zu erstellen, aber auch die Metatabelle der Tabelle hat eine __newindex Metamethode, möchten Sie die eingebaute Luau-Funktion rawset() verwenden, um den Wert ohne die Aktivierung von Metamethoden festzulegen.Nimm den folgenden Code als Beispiel dafür, was passiert, wenn du diese Funktionen nicht verwendest.
local t = setmetatable({}, {
__index = function(self, i)
self[i] = i * 10
return self[i]
end,
__newindex = function(self, i, v)
-- Setze keine Werte auf die Tabelle auf die normale Weise
end
})
print(t[1]) -- Causes a stack overflow
Stack-Überläufe passieren, wenn du versuchst, eine Funktion von sich aus zu oft aufzurufen.In der __index-Funktion oben wird self[i] auf einen Wert gesetzt, so dass, wenn es zur nächsten Zeile gelangt, self[i] existieren sollte und wahrscheinlich nicht die Metamethode __index aufrufen wird.Das Problem ist, dass __newindex dir nicht erlaubt, den Wert festzulegen.Seine Präsenz hindert Werte daran, mit der Standardmethode t[i] = v dem Tisch hinzugefügt zu werden.Um dies zu umgehen, verwende die rawset()-Funktion:
local t = setmetatable({}, {
__index = function(self, i)
rawset(self, i, i * 10)
return self[i]
end,
__newindex = function(self, i, v)
-- Setze keine Werte auf die Tabelle auf die normale Weise
end
})
print(t[1]) --> 10
Verwende den festgelegten Datentyp
Ein Set ist eine Sammlung von Elementen ohne Reihenfolge und ohne doppelte Elemente.Ein Element entweder ist oder ist nicht enthalten in einem festlegen.Mit Metatabellen kannst du Sätze innerhalb von Luau-Skripten konstruieren und manipulieren.
Grundlegende Methoden
Der folgende Code enthält die grundlegende Set-Funktionalität, mit der du neue Sätze erstellen, ein Artikelhinzufügen und entfernen, überprüfen kannst, ob ein Satz ein Artikelenthält, und den Inhalt eines festlegenausgeben kannst.
local Set = {}
Set.__index = Set
-- Funktion zum Erstellen eines Sets aus einer optionalen Liste von Artikeln
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 einer festlegenhinzuzufügen
function Set:add(item)
self[item] = true
end
-- Funktion zum Entfernen eines Elements aus einem festlegen
function Set:remove(item)
self[item] = nil
end
-- Funktion, um zu überprüfen, ob ein Set ein Artikelenthält
function Set:contains(item)
return self[item] == true
end
-- Funktion zum Ausgeben als kommabegrenzte Liste für die Fehlerbehebung
function Set:output()
local elems = {}
for key, value in self do
table.insert(elems, tostring(key))
end
print(table.concat(elems, ", "))
end
festlegenerstellen
Ein neues Set kann erstellt werden, indem Set.new() mit einem optionalen Array von Elementen angerufen wird, die hinzufügenwerden müssen.
local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})
Beachten Sie, dass ein Set per Definition kein Konzept der Reihenfolge hat.
Artikel hinzufügen
Das Hinzufügen eines Elements zu einem bestehenden Set kann über die Methode Set:add() erfolgen.
local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})fruits:add("Mango")
Artikel entfernen
Um ein Element aus einem festlegenzu entfernen, rufe Set:remove() mit dem Namen des Elements auf.
local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})fruits:remove("Orange")
Prüfen Sie auf Artikel
Um zu überprüfen, ob ein Set ein bestimmtes Artikelenthält, verwende 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, mit denen Sie Elemente zwischen Sätzen vergleichen, Sätze kombinieren oder einen Satz von einem anderen abziehen können.
Kreuzung
Wenn du Sätze als Venn-Diagramme betrachtest, kannst du die Intersektion von zwei Sätzen wie folgt erhalten, was bedeutet, dass die Elemente, die in beiden Sätzen erscheinen, gemeint sind.
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
Verbindung
Du kannst die Union von zwei Sätzen mit der folgenden Funktion erhalten, was bedeutet, eine Sammlung der Artikel in beiden Sätzen ohne Duplikaten.Beachten Sie, dass diese Funktion die Metamethode __add verwendet, um einen zusätzlichen Kurzweg von set1 + set2 bereitzustellen.
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, Limette, Apfel, Kirsche, Zitrone, Mango
Subtraktion
Du kannst alle Elemente in einem Set von den Elementen in einem anderen Set über die folgende Funktion entfernen.Ähnlich wie die Funktion oben verwendet dies die metatable __sub -Methode, um einen Subtraktionskürzer von set1 - set2 bereitzustellen.
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