Metatabele

*Questo contenuto è tradotto usando AI (Beta) e potrebbe contenere errori. Per visualizzare questa pagina in inglese, clicca qui.

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:

MetodoDescrizione
__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.
__metatableSe 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 <= ¹
__modeUtilizzato 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 do
print(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) -- vero
local 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