Le metatabelle consentono alle tabelle di diventare più potenti di prima.Sono allegati ai dati e contengono valori chiamati metodi metà.I metodi metametodi vengono invocati quando una certa azione viene utilizzata con il datum a cui è allegata.
Manipola tabelle metadati
Le due funzioni principali per aggiungere e trovare il metatable di una tabella sono setmetatable() e getmetatable() .
local x = {}local metaTable = {} -- Le tabelle meta sono tabelle, anche!setmetatable(x, metaTable) -- Dai x un metatable chiamato metaTable!print(getmetatable(x)) --> table: [hexadecimal memory address]
La funzione setmetatable() restituisce anche la tabella che stai impostando il metatable, quindi questi due script fanno la stessa cosa:
local x = {}setmetatable(x, {})
local x = setmetatable({}, {})
Metodici Metodi
I metodi metametodi sono le funzioni che vengono memorizzate all'interno di un metatable.Possono passare da una chiamata a un tavolo, all'aggiunta di un tavolo, fino a dividere anche i tavoli.Ecco l'elenco dei metodi disponibili:
Metodo | Descrizione |
---|---|
__index(table, index) | Si accende quando table[index] è indicizzato, se table[index] è nil. Può anche essere impostato su una tabella, in tal caso quella tabella verrà indicizzata. |
__newindex(table, index, value) | Si accende quando table[index] cerca di essere impostato (table[index] = value) , se table[index] è nil .Può anche essere impostato su una tabella, in tal caso quella tabella verrà indicizzata. |
__call(table, ...) | Si accende quando la tabella viene chiamata come una funzione, ... sono gli argomenti che sono stati passati. |
__concat(table, value) | Si accende quando l'operatore di concatenazione .. viene utilizzato sulla tabella. |
__unm(table) | Si accende quando l'operatore unario – viene utilizzato sulla tabella. |
__add(table, value) | L'operatore di aggiunta +. |
__sub(table, value) | L'operatore di sottrazione –. |
__mul(table, value) | L'operatore di moltiplicazione *. |
__div(table, value) | L'operatore di divisione /. |
__idiv(table, value) | L'operatore di divisione del pavimento //. |
__mod(table, value) | L'operatore modulo % . |
__pow(table, value) | L'operatore di esponenziazione ^. |
__tostring(table) | Spedito quando tostring viene chiamato sulla tabella. |
__metatable | Se presente, blocca il metatable in modo che getmetatable() restituisca questo invece del metatable e setmetatable() errore. Valore non funzionale. |
__eq(table, value) | Il == uguale all'operatore¹ |
__lt(table, value) | L'operatore < inferiore a ¹ |
__le(table, value) | L'operatore <= ¹ |
__mode | Utilizzato in tabelle deboli, per dichiarare se le chiavi e/o i valori di una tabella sono deboli.Nota che i riferimenti alle istanze di Roblox non sono mai deboli.Le tabelle che contengono tali riferimenti non verranno mai raccolte in garbage. |
__len(table) | Spedito quando l'operatore di lunghezza # viene utilizzato sull'oggetto. |
__iter(table) | Utilizzato per indicare un iteratore personalizzato quando si usa l'iterazione generale. |
Si noti che quando si scrivono funzioni per metodologie aritmetiche o relazionali, i due parametri della funzione sono intercambiabili tra la tabella che ha lanciato il metodo metodico e l'altro valore.Ad esempio, quando si eseguono operazioni vectoriali con divisione di scalari non è commutativa.Pertanto, se stavi scrivendo metodi metametodi per la tua stessa classe vector2, vorresti essere attento a tenere conto di uno o dell'altro scenario.
local vector2 = {__type = "vector2"}
local mt = {__index = vector2}
function mt.__div(a, b)
if type(a) == "number" then
-- a è uno scalare, b è un vettoriale
local scalar, vector = a, b
return vector2.new(scalar / vector.x, scalar / vector.y)
elseif type(b) == "number" then
-- a è un vettoriale, b è un scalar
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
-- entrambi a e b sono veicoli
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
Uso
Esistono molti modi per utilizzare le metatabelle, ad esempio il metodo __unm metametodo per rendere una tabella negativa:
local metatable = {
__unm = function(t) -- __unm è per l'operatore unario
local negated = {}
for key, value in t do
negated[key] = -value -- negare tutti i valori in questa tabella
end
return negated -- restituisci la tabella
end
}
local table1 = setmetatable({10, 11, 12}, metatable)
print(table.concat(-table1, "; ")) --> -10; -11; -12
Ecco un modo interessante per dichiarare le cose usando __index :
local metatable = {__index = {x = 1}}local t = setmetatable({}, metatable)print(t.x) --> 1
__index è stato licenziato quando x è stato indexato nella tabella e non trovato.Luau quindi ha cercato attraverso la tabella __index per un indice chiamato x e, trovandone uno, ha restituito quello.
Ora puoi facilmente farlo con una semplice funzione, ma c'è molto di più da dove proviene. Prendi questo per esempio:
local t = {10, 20, 30}print(t(5))
Di solito non puoi chiamare una tabella, ma con le metatabelle puoi:
local metatable = {
__call = function(t, param)
local sum = {}
for i, value in ipairs(t) do
sum[i] = value + param -- Aggiungi l'argomento (5) al valore, quindi posizionalo nella nuova tabella (t).
end
return unpack(sum) -- Restituisci i valori della tabella individuale
end
}
local t = setmetatable({10, 20, 30}, metatable)
print(t(5)) --> 15 25 35
Puoi fare molto di più, come aggiungere tabelle:
local table1 = {10, 11, 12}local table2 = {13, 14, 15}for k, v in table1 + table2 doprint(k, v)end
Questo errore dirà che stai tentando di eseguire un'aritmetica su una tabella, ma funziona quando viene tentata con un 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
Quando giochi con le metatabelle, potresti imbatterti in alcuni problemi.Se devi usare il metodo __index metametodo per creare nuovi valori in una tabella, ma anche il metodo metabile di quella tabella ha un metodo __newindex metametodo, vorrai usare la funzione Luau integrata rawset() per impostare il valore senza invocare alcun metodo metametodo.Prendi il seguente codice come esempio di ciò che succede se non usi queste funzioni.
local t = setmetatable({}, {
__index = function(self, i)
self[i] = i * 10
return self[i]
end,
__newindex = function(self, i, v)
-- Non impostare i valori alla tabella nel modo normale
end
})
print(t[1]) -- Causes a stack overflow
Gli errori di sovraccarico di stack accadono quando provi a chiamare una funzione da sola troppe volte.Nella funzione __index sopra, self[i] è impostata a un valore, quindi quando arriva alla prossima linea, self[i] dovrebbe esistere e presumibilmente non chiamerà il metodo __index metametodo.Il problema è che __newindex non ti consente di impostare il valore.La sua presenza impedisce l'aggiunta di valori alla tabella con il metodo standard t[i] = v.Per superare questo, usa la funzione rawset() :
local t = setmetatable({}, {
__index = function(self, i)
rawset(self, i, i * 10)
return self[i]
end,
__newindex = function(self, i, v)
-- Non impostare i valori alla tabella nel modo normale
end
})
print(t[1]) --> 10
Usa il tipo di dati impostato
Un set è una raccolta di elementi senza ordine e senza elementi duplici.Un oggetto o è o non è contenuto in un Impostare.Usando le metatabelle, puoi costruire e manipolare insiemi all'interno degli script Luau.
Metodi di base
Il seguente codice include la funzionalità di insieme di base, che ti consente di costruire nuovi insiemi, aggiungere e rimuovere un Articolo, controllare se un insieme contiene un Articoloe produrre i contenuti di un Impostare.
local Set = {}
Set.__index = Set
-- Funzione per costruire un set da una lista opzionale di oggetti
function Set.new(items)
local newSet = {}
for key, value in items or {} do
newSet[value] = true
end
return setmetatable(newSet, Set)
end
-- Funzione per aggiungere un oggetto a un Impostare
function Set:add(item)
self[item] = true
end
-- Funzione per rimuovere un oggetto da un Impostare
function Set:remove(item)
self[item] = nil
end
-- Funzione per controllare se un set contiene un Articolo
function Set:contains(item)
return self[item] == true
end
-- Funzione per l'output di set come elenco delimitato da virgola per la debugazione
function Set:output()
local elems = {}
for key, value in self do
table.insert(elems, tostring(key))
end
print(table.concat(elems, ", "))
end
Crea Impostare
Un nuovo set può essere costruito chiamando Set.new() con un array opzionale di elementi da aggiungere or Inserire.
local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})
Si noti che per definizione, un set non ha alcun concetto di ordinamento.
Aggiungi Articolo
Aggiungere un oggetto a un set esistente può essere fatto tramite il metodo Set:add().
local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})fruits:add("Mango")
Rimuovi Articolo
Per rimuovere un oggetto da un Impostare, chiama Set:remove() con il nome dell'oggetto.
local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})fruits:remove("Orange")
Controlla per l'Articolo
Per controllare se un set contiene un Articolospecifico, usa Set:contains() .
local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})local result1 = fruits:contains("Cherry")print(result1) -- verolocal result2 = fruits:contains("Watermelon")print(result2) -- false
Metodi aggiuntivi
Altre operazioni utili possono essere implementate per i set, consentendo di confrontare gli elementi tra i set, combinare i set o sottrarre un set ad un altro.
Intersezione
Quando si considerano i set come diagrammi di Venn, puoi ottenere l'intersezione di due set come segue, intendendo gli elementi che appaiono in entrambi i set.
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
Unione
Puoi ottenere l' unione di due set con la seguente funzione, che significa una raccolta degli elementi in entrambi i set senza duplicati.Nota che questa funzione utilizza il metodo metatable __add per fornire un scorciatoia di aggiunta di set1 + set2 .
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() -- Prugna, Lime, Mela, Ciliegia, Limone, Mango
Sottrazione
Puoi rimuovere tutti gli elementi in un set da quelli in un altro set attraverso la seguente funzione.Simile alla funzione sopra, questo utilizza il metodo metatable __sub per fornire un scorciatoia di sottrazione di set1 - set2 .
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