Metatábles

*Este conteúdo é traduzido por IA (Beta) e pode conter erros. Para ver a página em inglês, clique aqui.

Tabelas metálicas permitem que as tabelas se tornem mais poderosas do que antes.Eles são anexados a dados e contêm valores chamados metodos metálicos.Metodos metálicos são acionados quando uma determinada ação é usada com o datum a que está anexada.

Manipular tabelas metálicas

As duas funções principais para adicionar e encontrar o metadado de uma tabela são setmetatable() e getmetatable().


local x = {}
local metaTable = {} -- metatábuas são mesas, também!
setmetatable(x, metaTable) -- Dê x uma mesa metável chamada metaTable!
print(getmetatable(x)) --> table: [hexadecimal memory address]

A função setmetatable() também retorna a tabela que você está definindo o metatable, então esses dois scripts fazem a mesma coisa:


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

local x = setmetatable({}, {})

Metodos Metálicos

Metamétodos são as funções que são armazenadas dentro de um metatable.Eles podem ir de chamar uma tabela, adicionar uma tabela, até mesmo dividir tabelas também.Aqui está a lista de metodos disponíveis:

MétodoDescrição
__index(table, index)Incendeia quando table[index] é indexado, se table[index] for nil. Também pode ser definido para uma tabela, caso em que essa tabela será indexada.
__newindex(table, index, value)Incêndios quando table[index] tenta ser definido (table[index] = value), se table[index] for nil.Também pode ser definido para uma tabela, caso em que essa tabela será indexada.
__call(table, ...)Incêndios quando a tabela é chamada como uma função, ... são os argumentos que foram passados.
__concat(table, value)Dispara quando o operador de concatenação .. é usado na tabela.
__unm(table)Dispara quando o operador unário é usado na tabela.
__add(table, value)O operador de adição +.
__sub(table, value)O operador de subtração .
__mul(table, value)O operador de multiplicação *.
__div(table, value)O operador de divisão /.
__idiv(table, value)O operador de divisão de piso //.
__mod(table, value)O operador do módulo %.
__pow(table, value)O operador de exponenciação ^.
__tostring(table)Dispedido quando tostring é chamado na tabela.
__metatableSe presente, trava o metatable para que getmetatable() retorne isso em vez do metatable e setmetatable() retorne em erro. Valor não funcional.
__eq(table, value)O == igual ao operador¹
__lt(table, value)O operador < 1
__le(table, value)O operador <=¹
__modeUsado em tabelas fracas, declarando se as chaves e/ou valores de uma tabela são fracas.Observe que referências a instâncias do Roblox nunca são fracas.Tabelas que contêm tais referências nunca serão coletadas de lixo.
__len(table)Dispedido quando o operador de comprimento # é usado no Objeto.
__iter(table)Usado para denotar um iterador personalizado ao usar uma iteração generalizada.

Deve-se notar que ao escrever funções para metodos aritméticos ou relacionais metamétodos, os dois parâmetros de função são intercambiáveis entre a tabela que disparou o metodos metamétodos e o outro valor.Por exemplo, ao fazer operações de vetores com divisão de escalares não é commutativa.Portanto, se você estivesse escrevendo metodos metálicos para sua própria classe vector2, você gostaria de ter cuidado para contar com qualquer cenário.


local vector2 = {__type = "vector2"}
local mt = {__index = vector2}
function mt.__div(a, b)
if type(a) == "number" then
-- a é um escalar, b é um vetor
local scalar, vector = a, b
return vector2.new(scalar / vector.x, scalar / vector.y)
elseif type(b) == "number" then
-- a é um vetor, b é um 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 e b são vetores
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

Existem muitas maneiras de usar tabelas metálicas, por exemplo, o __unm metamétodo para tornar uma tabela negativa:


local metatable = {
__unm = function(t) -- __unm é para o operador unário
local negated = {}
for key, value in t do
negated[key] = -value -- negar todos os valores nesta tabela
end
return negated -- retornar a tabela
end
}
local table1 = setmetatable({10, 11, 12}, metatable)
print(table.concat(-table1, "; ")) --> -10; -11; -12

Aqui está uma maneira interessante de declarar coisas usando __index :


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

__index foi disparado quando x foi indexado na tabela e não encontrado.Luau então pesquisou através da tabela __index para um índice chamado x e, encontrando um, retornou isso.

Agora você pode facilmente fazer isso com uma função simples, mas há muito mais de onde isso veio. Tome isso como exemplo:


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

Normalmente, você não pode chamar uma tabela, mas com metatabelas você pode:


local metatable = {
__call = function(t, param)
local sum = {}
for i, value in ipairs(t) do
sum[i] = value + param -- Adicione o argumento (5) ao valor, em seguida, coloque-o na nova tabela (t).
end
return unpack(sum) -- Retorne os valores de tabela individuais
end
}
local t = setmetatable({10, 20, 30}, metatable)
print(t(5)) --> 15 25 35

Você também pode fazer muito mais, como adicionar tabelas:


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

Isso vai dar erro dizendo que você está tentando fazer aritmética em uma tabela, mas funciona quando tentado com um 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

Ao jogar com metatábuas, você pode encontrar alguns problemas.Se você precisar usar o __index metamétodo para criar novos valores em uma tabela, mas a tabela metável também tem um __newindex metamétodo, você vai querer usar a função integrada Luau rawset() para definir o valor sem invocar nenhum metamétodo.Tome o seguinte código como exemplo do que acontece se você não usar essas funções.


local t = setmetatable({}, {
__index = function(self, i)
self[i] = i * 10
return self[i]
end,
__newindex = function(self, i, v)
-- Não defina valores para a tabela da maneira normal
end
})
print(t[1]) -- Causes a stack overflow

Os excessos de pilha acontecem quando você tenta chamar uma função de si mesma muitas vezes demais.Na função __index acima, self[i] é definido para um valor, então, quando chegar à próxima linha, self[i] deve existir e presumivelmente não chamará o metamétodo __index.O problema é que __newindex não permite que você defina o valor.Sua presença impede que valores sejam adicionados à tabela com o método padrão t[i] = v.Para superar isso, use a função rawset():


local t = setmetatable({}, {
__index = function(self, i)
rawset(self, i, i * 10)
return self[i]
end,
__newindex = function(self, i, v)
-- Não defina valores para a tabela da maneira normal
end
})
print(t[1]) --> 10

Use o tipo de dado definido

Um set é uma coleção de itens sem ordem e sem elementos duplicados.Um item é ou não é contido dentro de um configurar.Usando tabelas metálicas, você pode construir e manipular conjuntos dentro de scripts Luau.

Metodos básicos

O seguinte código inclui a funcionalidade básica de definição de conjuntos, permitindo que você construa novos conjuntos, adicione e remova um item, verifique se um conjunto contém um item e saia o conteúdo de um configurar.


local Set = {}
Set.__index = Set
-- Função para construir um conjunto a partir de uma lista opcional de itens
function Set.new(items)
local newSet = {}
for key, value in items or {} do
newSet[value] = true
end
return setmetatable(newSet, Set)
end
-- Função para adicionar um item a um configurar
function Set:add(item)
self[item] = true
end
-- Função para remover um item de um configurar
function Set:remove(item)
self[item] = nil
end
-- Função para verificar se um conjunto contém um item
function Set:contains(item)
return self[item] == true
end
-- Função para exportar conjunto como uma lista delimitada por vírgula para depuração
function Set:output()
local elems = {}
for key, value in self do
table.insert(elems, tostring(key))
end
print(table.concat(elems, ", "))
end

Criar configurar

Um novo conjunto pode ser construído chamando Set.new() com um conjunto opcional de itens a adicionar.


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

Observe que, por definição, um conjunto não tem conceito de ordem.

Adicione um item

Adicionar um item a um conjunto existente pode ser feito através do método Set:add().


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

Remover item

Para remover um item de um configurar, chame Set:remove() com o nome do item.


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

Verificar item

Para verificar se um conjunto contém um item específico, use Set:contains() .


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

Métodos adicionais

Outras operações úteis podem ser implementadas para conjuntos, permitindo que você compare itens entre conjuntos, combine conjuntos ou subtraia um conjunto de outro.

Interseção

Ao considerar conjuntos como diagramas de Venn, você pode obter a interseção de dois conjuntos da seguinte forma, significando os itens que aparecem em ambos os 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ão

Você pode obter a união de dois conjuntos com a seguinte função, o que significa uma coleção de itens em ambos os conjuntos sem duplicados.Observe que essa função usa o método metatable __add para fornecer um atalho 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() -- Pêssego, Lima, Maçã, Cereja, Limão, Manga

Subtração

Você pode remover todos os itens em um conjunto dos itens em outro conjunto através da função a seguir.Semelhante à função acima, isso usa o método metatable __sub para fornecer um atalho de subtração 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