I metatabelle consentono alle tabelle di diventare più potenti di prima. Sono allegati ai dati e contengono valori chiamati metametodi. I metametodi vengono attivati quando viene utilizzata una certa azione con il datum a cui è allegato.
Considera il seguente codice:
local list = {1, 2}print(list[3])
Potresti aspettarti che questo codice cerci attraverso la lista per l'indice di terza generazione, trovi nulla e restituisca nil. Ciò non è corretto, però. Ciò che accade in realtà è che il codice cerca attraverso la lista per l'indice di terza generazione, trova nulla e poi controlla se c'è un metitable allegato alla tabella, che restituisce nil se non c'è uno.
Manipolazione delle metatabelle
Le due funzioni principali per l'aggiunta e la ricerca di una tabella mettabile, sono setmetatable e getmetatable
local x = {}local metaTable = {} -- Le tabelle metaTables sono tabelle, anche!setmetatable(x, metaTable) -- Dai x una mettabile chiamata metaTable!print(getmetatable(x)) --> table: [hexadecimal memory address]
La funzione setmetatable restituisce anche la tabella che stai impostando, quindi questi due script fanno la stessa cosa:
local x = {}setmetatable(x, {})
local x = setmetatable({}, {})
Metodi di metametodo
I metodi metametodi sono le funzioni che sono memorizzate all'interno di una metatavola. Possono andare da chiamare una tabella, aggiungere una tabella, persino dividere le tabelle. Ecco il elenco delle metodi metametodi disponibili:
Metodo | Descrizione |
---|---|
__index(tabella, indice) | Si attiva quando la tabella [index] è indizzata, se la tabella [index] è nulla. Può essere impostato anche su una tabella, in cui caso quella tabella sarà indizzata. |
__newindex(tabella, indice, valore) | Si attiva quando la tabella [index] cerca di essere impostata (tabella [index] = valore), se la tabella [index] è vuota. Può essere impostato anche su una tabella, in cui caso quella tabella sarà indizzata. |
__call(tabella, ...) | Si attiva quando la tabella viene chiamata come una funzione, ... sono gli argomenti che sono stati passati. |
__concat(tabella, valore) | Si attiva quando viene utilizzato l'operatore di concatenazione .. sulla tabella. |
__unm(tabella) | Si attiva quando l'operatore unario viene utilizzato sulla tabella. |
__add(tabella, valore) | L'operatore +. |
__sub(tabella, valore) | Il - operatore di sottrazione. |
__mul(tabella, valore) | L'operatore * di moltiplicazione. |
__div(tabella, valore) | L'operatore / divisione. |
__idiv(tabella, valore) | Il // operatore di divisione del piano. |
__mod(tabella, valore) | L'operatore modulo:%. |
__pow(tabella, valore) | L'operatore ^ espansione. |
__tostring(tabella) | Fired quando tostring è chiamato sulla tabella. |
__metitable | Se presente, blocca la mettabile in modo che getmettabile restituisca questo invece della mettabile e setmettabile che si verrà errore. Non valore funzionale. |
__eq(tabella, valore) | Il == pari a operatore¹ |
__lt(tabella, valore) | Il < meno di operator¹ |
__le(tabella, valore) | Il operatore¹ |
modalità | Utilizzato in tabelle deboli, dichiarando se le chiavi e/o i valori di una tabella sono deboli. Nota: I riferimenti a istanze Roblox non sono mai deboli. Le tabelle che contengono tali riferimenti non saranno mai raccolte. |
__gc(tabella) | Si esegue quando la tabella è stata salvata. Nota: Su Roblox, questo metodo metametodo è disabilitato. |
__len(tabella) | Fired quando l'operatore di lunghezza # viene utilizzato sull'oggetto. |
__iter(tabella) | Utilizzato per designare un innovatore personalizzato quando si utilizza l'iterazione generica. |
Si dovrebbe notare che quando si scrivono funzioni per metodi aritmetici o relazionali i due parametri di funzione sono scambiabili tra la tabella che ha eseguito il metodo e l'altro valore. Ad esempio, quando si eseguono operazioni di matrice con divisione scala non è committente. Quindi, se stavi scrivendo metodi per il tuo classe veicolo2, dovresti essere attento a tenere conto di entrambi gli scenari.
local vector2 = {__type = "vector2"}
local mt = {__index = vector2}
function mt.__div(a, b)
if type(a) == "number" then
-- a è un valore, 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 vecchie
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
Uso dei Metatabel
Ci sono molti modi per utilizzare i metatabel, ad esempio il metodi __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 attivato quando x è stato indexed in the table and not found. Lua poi ha cercato attraverso la tabella __index per un indice chiamato x, e, trovando uno, ha restituito che.
Ora puoi facilmente fare quello con una semplice funzione, ma c'è molto di più da dove è venuto. Prendi questo per esempio:
local t = {10, 20, 30}print(t(5))
Ora, ovviamente, non puoi chiamare una tabella. Questo è pazzesco, ma (sorpresa, sorpresa!) con i 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 individuali della tabella
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 avrà un errore dicendo che stai cercando di eseguire aritmetica su una tabella. Probabilmente proviamo questo 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
Usa casi d'uso
Ora, tutti questi esempi possono essere implementati come una semplice funzione, ma puoi fare molto di più. Probabilmente, prova un programma semplice che memorizza un numero quando un possibile problema matematico laggy viene inserito in esso.
Per questo useremo il metodo __index metametodo per renderlo semplice:
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]) -- Sarà lento poiché è la prima volta che si usa questo numero, quindi deve eseguire la funzione di matematica.
print(t[2]) -- sarà lento poiché è la prima volta che si utilizza questo numero.
print(t[1]) -- will be fast because it's just grabbing the number from the table.
Rawset, Rawget, Rawequal
Quando giochi con metatabelle, potresti imbatterti in alcuni problemi. Cosa succede se devi usare il metatabelle __index metamето per creare nuovi valori in una tabella, ma anche il metatabelle di quella tabella ha un metodo __newindex metameto in esso? Vuoi usare la funzione built-in Lua rawset
local t = setmetatable({}, {
__index = function(self, i)
self[i] = i * 10 -- solo come esempio
return self[i]
end,
__newindex = function(self, i, v)
--non fare nulla perché non vogliamo che tu impostare i valori alla tabella nel modo normale
end
})
print(t[1]) -- Causes a C-Stack overflow
Ora perché dovrebbe causare un sovrastrappolamento? Gli sovrastrappolamenti si verificano quando cerchi di chiamare una funzione da te troppi volte, ma cosa potrebbe causare che accada? Nella funzione __index, impostiamo self[i] su un valore, quindi quando viene alla prossima riga, self[
Il problema è che __newindex non ci consente di impostare il valore. La sua presenza impedisce che i valori vengano aggiunti alla tabella con il metodo standard t[i] = v. Per superare questo, usi 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 fare nulla perché non vogliamo che tu impostare i valori alla tabella nel modo normale
end
})
print(t[1]) -- prints 10
Uso del Set Datatype
Un set è una collezione di oggetti senza ordine e senza elementi duplicati. Un oggetto è o non è contenuto in un Impostare. Utilizzando metatabelle, puoi costruire e manipolare set all'interno degli script Lua.
Metodi di base
Il seguente codice include la funzionalità di base, consentendoti di costruire nuovi set, aggiungere e rimuovere un Articolo, controllare se un set contiene un Articoloe generare i contenuti di un Impostare.
local Set = {}
Set.__index = Set
-- Funzione per costruire un set da un'opzionale lista 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 elemento a un Impostare
function Set:add(item)
self[item] = true
end
-- Funzione per rimuovere un elemento 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 esportare la lista come una lista a virgola per il debug
function Set:output()
local elems = {}
for key, value in self do
table.insert(elems, tostring(key))
end
print(table.concat(elems, ", "))
end
Crea Set
Un nuovo set può essere costruito chiamando Set.new() con un' array opzionale di oggetti da aggiungere or Inserire.
local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})
Nota che per definizione, un set non ha concetto di ordinamento.
Aggiungi oggetto
L'aggiungimento di un elemento a un set esistente può essere eseguito tramite il metodo Set:add() .
local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})fruits:add("Mango")
Rimuovi oggetto
Per rimuovere un elemento da un Impostare, chiama Set:remove() con il nome dell'elemento.
local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})fruits:remove("Orange")
Controlla per oggetto
Per verificare 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, consentendoti di confrontare gli elementi tra i set, combinare set o sottrarre uno set dall'altro.
Intersezione
Quando si considera set come Venn diagrammi, si può ottenere l'intersezione di due set come segue, il che significa che gli elementi che appaiono in entrambi 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 usa il metitable __add metodo per fornire un breve scorciatoia di aggiungere di __add .
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() -- Peach, Lime, Apple, Cherry, Lemon, Mango
Sottrazione
Puoi rimuovere tutti gli elementi in un set da tutti gli elementi in un altro set tramite la funzione seguente. Come la funzione sopra, questo utilizza il mettabile __sub metodo per fornire una 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