Las tablas metálicas permiten que las tablas se vuelvan más poderosas que antes.Están asociados a datos y contienen valores llamados metodos metálicos.Los metamétodos se disparan cuando se usa una cierta acción con el datum al que está asociado.
Manipular tablas metálicas
Las dos funciones principales para agregar y encontrar la tabla metatable son setmetatable() y getmetatable() .
local x = {}local metaTable = {} -- ¡Las tablas meta son tablas también!setmetatable(x, metaTable) -- ¡Dale a x una tabla metálica llamada tabla metálica!print(getmetatable(x)) --> table: [hexadecimal memory address]
La función setmetatable() también devuelve la tabla que estás configurando el metatable, por lo que estos dos scripts hacen lo mismo:
local x = {}setmetatable(x, {})
local x = setmetatable({}, {})
Metodos metálicos
Los metamétodos son las funciones que se almacenan dentro de una tabla metálica.Pueden ir desde llamar una tabla, hasta agregar una tabla, e incluso dividir tablas también.Aquí está la lista de metodos disponibles:
Método | Descripción |
---|---|
__index(table, index) | Se enciende cuando table[index] está indexado, si table[index] es nil . También se puede configurar para una tabla, en cuyo caso esa tabla se indexará. |
__newindex(table, index, value) | Se enciende cuando table[index] intenta ser establecido (table[index] = value) , si table[index] es nil .También se puede configurar para una tabla, en cuyo caso esa tabla se indexará. |
__call(table, ...) | Se incendia cuando la tabla se llama como una función, ... son los argumentos que se pasaron. |
__concat(table, value) | Se enciende cuando el operador de concatenación .. se usa en la tabla. |
__unm(table) | Se activa cuando se usa el operador unario – en la tabla. |
__add(table, value) | El operador de adición +. |
__sub(table, value) | El operador de resta –. |
__mul(table, value) | El operador de multiplicación *. |
__div(table, value) | El operador de división /. |
__idiv(table, value) | El operador de división de piso //. |
__mod(table, value) | El operador de módulo %. |
__pow(table, value) | El operador de exponente ^ . |
__tostring(table) | Se desfue cuando se llama tostring en la tabla. |
__metatable | Si está presente, bloquea el metatable para que getmetatable() devuelva esto en lugar del metatable y setmetatable() devuelva un error. Valor no funcional. |
__eq(table, value) | El == igual al operador¹ |
__lt(table, value) | El operador < ¹ |
__le(table, value) | El operador <=¹ |
__mode | Se usa en tablas débiles, para declarar si las claves y/o valores de una tabla son débiles.Tenga en cuenta que las referencias a instancias de Roblox nunca son débiles.Las tablas que contienen tales referencias nunca se recolectarán como basura. |
__len(table) | Se despedida cuando se usa el operador de longitud # en el objeto. |
__iter(table) | Se usa para denotar un iterador personalizado al usar iteración generalizada. |
Cabe señalar que al escribir funciones para metamétodos aritméticos o relacionales, los dos parámetros de función son intercambiables entre la tabla que disparó el metamétodo y el otro valor.Por ejemplo, al hacer operaciones vectoriales con división de escalares no es commutativa.Por lo tanto, si estabas escribiendo metamétodos para tu propia clase vector2, querrás tener cuidado de tener en cuenta cualquiera de los escenarios.
local vector2 = {__type = "vector2"}
local mt = {__index = vector2}
function mt.__div(a, b)
if type(a) == "number" then
-- a es un escalar, b es un fuerza vectorial
local scalar, vector = a, b
return vector2.new(scalar / vector.x, scalar / vector.y)
elseif type(b) == "number" then
-- a es un fuerza vectorial, b es un escalar
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
-- ambos a y b son vectores
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
Hay muchas maneras de usar tablas metálicas, por ejemplo, el __unm metamétodo para hacer una tabla negativa:
local metatable = {
__unm = function(t) -- __unm es para el operador unario
local negated = {}
for key, value in t do
negated[key] = -value -- negar todos los valores en esta tabla
end
return negated -- 返回 la tabla
end
}
local table1 = setmetatable({10, 11, 12}, metatable)
print(table.concat(-table1, "; ")) --> -10; -11; -12
Aquí hay una manera interesante de declarar cosas usando __index :
local metatable = {__index = {x = 1}}local t = setmetatable({}, metatable)print(t.x) --> 1
__index se disparó cuando x se indexó en la tabla y no se encontró.Luau luego buscó a través de la tabla __index para un índice llamado x , y, encontrando uno, lo devolvió.
Ahora puedes hacer eso fácilmente con una función simple, pero hay mucho más de donde vino eso. Toma esto por ejemplo:
local t = {10, 20, 30}print(t(5))
Típicamente no puedes llamar una tabla, pero con tablas metálicas puedes:
local metatable = {
__call = function(t, param)
local sum = {}
for i, value in ipairs(t) do
sum[i] = value + param -- Añade el argumento (5) al valor, luego colócalo en la nueva tabla (t).
end
return unpack(sum) -- Devolver los valores de tabla individuales
end
}
local t = setmetatable({10, 20, 30}, metatable)
print(t(5)) --> 15 25 35
También puedes hacer mucho más, como agregar tablas:
local table1 = {10, 11, 12}local table2 = {13, 14, 15}for k, v in table1 + table2 doprint(k, v)end
Esto fallará diciendo que estás tratando de realizar aritmética en una tabla, pero funciona cuando se intenta con una tabla 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
Al jugar con tablas metálicas, puedes encontrarte con algunos problemas.Si necesita usar el metamétodo __index para crear nuevos valores en una tabla, pero el metamétodo de esa tabla también tiene un metamétodo __newindex, querrá usar la función integrada de Luau rawset() para establecer el valor sin invocar ningún metamétodo.Toma el siguiente código como ejemplo de lo que sucede si no usas estas funciones.
local t = setmetatable({}, {
__index = function(self, i)
self[i] = i * 10
return self[i]
end,
__newindex = function(self, i, v)
-- No establezca valores a la tabla de la manera normal
end
})
print(t[1]) -- Causes a stack overflow
Los excesos de pila ocurren cuando intentas llamar a una función desde sí misma demasiadas veces.En la función __index arriba, self[i] se establece a un valor, por lo que cuando llegue a la siguiente línea, self[i] debería existir y presumiblemente no llamará al metamétodo __index.El problema es que __newindex no te deja establecer el valor.Su presencia detiene que se agreguen valores a la tabla con el método estándar t[i] = v.Para superar esto, utilice la función rawset():
local t = setmetatable({}, {
__index = function(self, i)
rawset(self, i, i * 10)
return self[i]
end,
__newindex = function(self, i, v)
-- No establezca valores a la tabla de la manera normal
end
})
print(t[1]) --> 10
Usa el tipo de datos establecido
Un conjunto es una colección de elementos sin orden y sin elementos duplicados.Un artículo o es o no está contenido dentro de un establecer.Al usar tablas metálicas, puedes construir y manipular conjuntos dentro de los scripts de Luau.
Métodos básicos
El siguiente código incluye la funcionalidad básica de conjuntos, que te permite construir nuevos conjuntos, agregar y eliminar un objeto, verificar si un conjunto contiene un objetoy emitir el contenido de un establecer.
local Set = {}
Set.__index = Set
-- Función para construir un conjunto a partir de una lista opcional de elementos
function Set.new(items)
local newSet = {}
for key, value in items or {} do
newSet[value] = true
end
return setmetatable(newSet, Set)
end
-- Función para agregar un artículo a un establecer
function Set:add(item)
self[item] = true
end
-- Función para eliminar un elemento de un establecer
function Set:remove(item)
self[item] = nil
end
-- Función para comprobar si un conjunto contiene un objeto
function Set:contains(item)
return self[item] == true
end
-- Función para exportar como lista delimitada por comas para depurar
function Set:output()
local elems = {}
for key, value in self do
table.insert(elems, tostring(key))
end
print(table.concat(elems, ", "))
end
Crear establecer
Se puede construir un nuevo conjunto llamando Set.new() con un array opcional de elementos para añadir.
local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})
Tenga en cuenta que, por definición, un conjunto no tiene ningún concepto de orden.
Añadir objeto
Añadir un artículo a un conjunto existente se puede hacer a través del método Set:add().
local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})fruits:add("Mango")
Quitar objeto
Para eliminar un elemento de un establecer, llame a Set:remove() con el nombre del elemento.
local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})fruits:remove("Orange")
Compruebe el objeto
Para verificar si un conjunto contiene un objetoespecífico, use Set:contains() .
local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})local result1 = fruits:contains("Cherry")print(result1) -- ciertolocal result2 = fruits:contains("Watermelon")print(result2) -- false
Métodos adicionales
Otras operaciones útiles se pueden implementar para conjuntos, que te permiten comparar artículos entre conjuntos, combinar conjuntos o restar un conjunto de otro.
Intersección
Al considerar conjuntos como diagramas de Venn, puedes obtener la intersección de dos conjuntos como sigue, lo que significa que los elementos que aparecen en ambos conjuntos .
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
Unión
Puedes obtener la unión de dos conjuntos con la siguiente función, lo que significa una colección de los artículos de ambos conjuntos sin duplicados.Tenga en cuenta que esta función utiliza el método metatable __add para proporcionar un atajo adicional de 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() -- Melocotón, Lima, Manzana, Cereza, Limón, Mango
Restitución
Puedes eliminar todos los elementos en un conjunto de los elementos en otro conjunto a través de la siguiente función.Al igual que la función anterior, esto usa el método metatable __sub para proporcionar un atajo de resta de 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