Metatablas

*Este contenido se traduce usando la IA (Beta) y puede contener errores. Para ver esta página en inglés, haz clic en aquí.

Las metatablas permiten que las tablas sean más poderosas que antes. Se adjuntan a los datos y contienen valores llamados metamétodos. Los metamétodos se activan cuando se utiliza una acción determinada con el dato al que están adjuntos.

Manipular metatablas

Las dos funciones principales para agregar y encontrar la metatabla de una tabla son setmetatable() y getmetatable().

local x = {}
local metaTable = {} -- ¡Las metatablas también son tablas!
setmetatable(x, metaTable) -- ¡Dale a x una metatabla llamada metaTable!
print(getmetatable(x)) --> table: [dirección de memoria hexadecimal]

La función setmetatable() también devuelve la tabla a la que estás configurando la metatabla, por lo que estos dos scripts hacen lo mismo:

local x = {}
setmetatable(x, {})
local x = setmetatable({}, {})

Metamétodos

Los metamétodos son las funciones que se almacenan dentro de una metatabla. Pueden ir desde llamar a una tabla, hasta sumar una tabla, e incluso dividir tablas también. Aquí está la lista de metamétodos disponibles:

MétodoDescripción
__index(table, index)Se activa cuando se indexa table[index], si table[index] es nil. También se puede establecer en una tabla, en cuyo caso esa tabla será indexada.
__newindex(table, index, value)Se activa cuando se intenta establecer table[index] (table[index] = value), si table[index] es nil. También se puede establecer en una tabla, en cuyo caso esa tabla será indexada.
__call(table, ...)Se activa cuando la tabla se llama como una función, ... son los argumentos que se pasaron.
__concat(table, value)Se activa cuando se utiliza el operador de concatenación .. en la tabla.
__unm(table)Se activa cuando se utiliza el operador unario en la tabla.
__add(table, value)El operador de adición +.
__sub(table, value)El operador de sustracción .
__mul(table, value)El operador de multiplicación *.
__div(table, value)El operador de división /.
__idiv(table, value)El operador de división entera //.
__mod(table, value)El operador de módulo %.
__pow(table, value)El operador de potenciación ^.
__tostring(table)Se activa cuando se llama a tostring en la tabla.
__metatableSi está presente, bloquea la metatabla para que getmetatable() devuelva esto en lugar de la metatabla y setmetatable() genere un error. Valor no funcional.
__eq(table, value)El operador de igualdad ==¹
__lt(table, value)El operador de menor que <¹
__le(table, value)El operador de menor o igual que <=¹
__modeUtilizado en tablas débiles, declarando si las claves y/o valores de una tabla son débiles. Ver Usar tablas débiles.
__len(table)Se activa cuando se utiliza el operador de longitud # en el objeto.
__iter(table)Utilizado 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 activó el metamétodo y el otro valor. Por ejemplo, al realizar operaciones vectoriales con escalares, la división no es conmutativa. Por lo tanto, si estuvieras escribiendo metamétodos para tu propia clase vector2, querrías tener cuidado de tener en cuenta ambos 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 vector
local scalar, vector = a, b
return vector2.new(scalar / vector.x, scalar / vector.y)
elseif type(b) == "number" then
-- a es un vector, 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
-- tanto a como 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 metatablas.

Aplicar el operador unario menos a las tablas

El metamétodo __unm maneja el operador unario menos. Úsalo para negar cada valor en una tabla:

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 -- devolver la tabla
end
}
local table1 = setmetatable({10, 11, 12}, metatable)
print(table.concat(-table1, "; ")) --> -10; -11; -12

Indexar valores faltantes desde otra tabla

Aquí hay una forma interesante de declarar cosas usando __index:

local metatable = {
__index = {x = 1}
}
local t = setmetatable({}, metatable)
print(t.x) --> 1

__index se activó cuando se indexó x en la tabla y no se encontró. Luau luego buscó en la tabla __index un índice llamado x, y, al encontrar uno, lo devolvió.

Llamar a una tabla como una función

Las metatablas pueden hacer mucho más que proporcionar un valor faltante. Toma este ejemplo:

local t = {10, 20, 30}
print(t(5))

Normalmente no puedes llamar a una tabla, pero con metatablas puedes:

local metatable = {
__call = function(t, param)
local sum = {}
for i, value in ipairs(t) do
sum[i] = value + param -- Sumar el argumento (5) al valor, luego colocarlo en la nueva tabla (t).
end
return unpack(sum) -- Devolver los valores individuales de la tabla
end
}
local t = setmetatable({10, 20, 30}, metatable)
print(t(5)) --> 15 25 35

Aplicar el operador de adición a las tablas

Puedes hacer mucho más también, como sumar tablas:

local table1 = {10, 11, 12}
local table2 = {13, 14, 15}
for k, v in table1 + table2 do
print(k, v)
end

Esto generará un error diciendo que estás intentando realizar aritmética en una tabla, pero funciona cuando se intenta con una metatabla:

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

Establecer valores sin invocar metamétodos

Al jugar con metatablas, puedes encontrar algunos problemas. Si necesitas usar el metamétodo __index para crear nuevos valores en una tabla, pero la metatabla de esa tabla también tiene un metamétodo __newindex, querrás usar la función incorporada de Luau rawset() para establecer el valor sin invocar ningún metamétodo. Toma el siguiente código como un 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 establecer valores en la tabla de la manera normal
end
})
print(t[1]) -- Causa un desbordamiento de pila

Los desbordamientos de pila ocurren cuando intentas llamar a una función desde sí misma demasiadas veces. En la función __index anterior, self[i] se establece en un valor, por lo que cuando llega 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 permite establecer el valor. Su presencia detiene los valores de ser añadidos a la tabla con el método estándar t[i] = v. Para superar esto, usa 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 establecer valores en la tabla de la manera normal
end
})
print(t[1]) --> 10

Usar tablas débiles

Las variables que contienen tablas, funciones, hilos y userdata, incluyendo Instances, almacenan referencias a esos objetos en lugar de copias de ellos. Asignar un objeto a otra variable crea otra referencia al mismo objeto:

local map = {key = 1}
local mapReference = map
print(map == mapReference) --> true
print(map == {key = 1}) --> false
local function func() end
local funcReference = func
print(func == funcReference) --> true
local part = Instance.new("Part")
local partReference = part
print(part == partReference) --> true

Los objetos que ya no son accesibles a través de referencias fuertes se vuelven elegibles para la recolección de basura. Establecer una variable en nil no hace que un objeto sea elegible para la recolección de basura si otra referencia fuerte aún apunta a ese objeto:

local child = {}
local parent = {Child = child}
child = nil
print(parent.Child) --> table: [dirección de memoria hexadecimal]

Aunque child ya no se refiere a la tabla hijo, parent.Child aún lo hace. Mientras la tabla padre siga siendo accesible, su referencia mantiene la tabla hijo accesible también.

Una tabla débil mantiene claves, valores o ambos sin impedir que el recolector de basura reclame objetos que de otro modo serían inalcanzables. Establece el campo __mode de la metatabla de la tabla para controlar qué referencias son débiles:

  • "k" hace que las claves sean débiles.
  • "v" hace que los valores sean débiles.
  • "kv" o "vk" hace que tanto las claves como los valores sean débiles.

El siguiente ejemplo de código crea una tabla con valores débiles:

local children = setmetatable({}, {
__mode = "v",
})
do
local child = {}
children.example = child
end

Después de que finaliza el bloque do, el valor almacenado en children.example no tiene referencias fuertes y se vuelve elegible para la recolección de basura. Cuando el recolector de basura reclama la tabla hijo, Luau elimina su entrada de children. La recolección de basura es no determinista, así que no dependas de cuándo desaparece la entrada.

Cualquier modo que dejes fuera permanece fuerte. Las claves en una tabla __mode = "v" siguen siendo referencias fuertes, por lo que un objeto almacenado como clave permanece accesible y su entrada permanece en la tabla.

Para Instances, la recolección de basura se aplica a la referencia de Luau en lugar de al objeto en sí. El motor almacena datos de instancia fuera de la VM de Luau, por lo que recolectar una referencia de Luau no elimina la instancia, y una instancia que permanece en el modelo de datos no mantiene viva su referencia de Luau. Para liberar una instancia, llama a Instance:Destroy() y limpia todas las referencias restantes a ella.

Usar el tipo de dato conjunto

Un conjunto es una colección de elementos sin orden y sin elementos duplicados. Un elemento está o no está contenido dentro de un conjunto. Usando metatablas, puedes construir y manipular conjuntos dentro de scripts de Luau.

Métodos básicos

El siguiente código incluye funcionalidad básica de conjunto, permitiéndote construir nuevos conjuntos, agregar y eliminar un elemento, verificar si un conjunto contiene un elemento y mostrar el contenido de un conjunto.

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 elemento a un conjunto
function Set:add(item)
self[item] = true
end
-- Función para eliminar un elemento de un conjunto
function Set:remove(item)
self[item] = nil
end
-- Función para verificar si un conjunto contiene un elemento
function Set:contains(item)
return self[item] == true
end
-- Función para mostrar el conjunto como una lista delimitada por comas para depuración
function Set:output()
local elems = {}
for key, value in self do
table.insert(elems, tostring(key))
end
print(table.concat(elems, ", "))
end

Crear conjunto

Un nuevo conjunto se puede construir llamando a Set.new() con un array opcional de elementos para agregar.

local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})

Ten en cuenta que, por definición, un conjunto no tiene concepto de orden.

Agregar elemento

Agregar un elemento 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")

Eliminar elemento

Para eliminar un elemento de un conjunto, llama a Set:remove() con el nombre del elemento.

local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})
fruits:remove("Orange")

Verificar elemento

Para verificar si un conjunto contiene un elemento específico, usa Set:contains().

local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})
local result1 = fruits:contains("Cherry")
print(result1) -- true
local result2 = fruits:contains("Watermelon")
print(result2) -- false

Métodos adicionales

Otras operaciones útiles se pueden implementar para conjuntos, permitiéndote comparar elementos 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 de la siguiente manera, es decir, 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, es decir, una colección de los elementos en ambos conjuntos sin duplicados. Ten en cuenta que esta función utiliza el método de metatabla __add para proporcionar un atajo de adición 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() -- Peach, Lime, Apple, Cherry, Lemon, Mango

Sustracción

Puedes eliminar todos los elementos en un conjunto de los elementos en otro conjunto a través de la siguiente función. Similar a la función anterior, esto utiliza el método de metatabla __sub para proporcionar un atajo de sustracción 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
©2026 Roblox Corporation. Roblox, el logotipo de Roblox y "Powering Imagination" son algunas de nuestras marcas registradas y no registradas en los Estados Unidos y otros países.