Metatabelki pozwalają tablicom stać się bardziej potężnymi niż wcześniej.Są przymocowane do danych i zawierają wartości nazywane metodami hybrydowymi.Metametody są uruchamiane, gdy pewna akcja jest używana z datą, do której jest przypisana.
Manipuluj tablicami metodami
Dwie główne funkcje dodawania i znajdowania metatabli w tabeli to setmetatable() i getmetatable().
local x = {}local metaTable = {} -- Tabele meta są również tablicami!setmetatable(x, metaTable) -- Daj x metabeli nazywanej metaTable!print(getmetatable(x)) --> table: [hexadecimal memory address]
Funkcja setmetatable() zwrotuje również tabelę, na której ustawiasz metatabelę, więc te dwa skrypty robią to samo:
local x = {}setmetatable(x, {})
local x = setmetatable({}, {})
Metody metametryczne
Metametody są funkcjami przechowywanymi w metabeli.Mogą przejść od wezwania tabeli, po dodanie tabeli, aż po podzielenie tabel również.Oto lista dostępnych metod meta:
Metoda | Opis |
---|---|
__index(table, index) | Wypala się, gdy table[index] jest indeksowany, jeśli table[index] jest nil. Można również ustawić do tabeli, w której wówczas tabela zostanie indeksowana. |
__newindex(table, index, value) | Wypala się, gdy table[index] próbuje być ustawiony (table[index] = value), jeśli table[index] jest nil.Można również ustawić do tabeli, w której przypadku tabela zostanie indeksowana. |
__call(table, ...) | Ogień, gdy tabela jest wzywana jak funkcja, ... to są argumenty, które zostały przekazane. |
__concat(table, value) | Wypala się, gdy operator łączenia .. jest używany na tabeli. |
__unm(table) | Wystrzeliwuje, gdy operator jednolity – jest używany na tabeli. |
__add(table, value) | Operator dodawania + . |
__sub(table, value) | Operator odejmowania –. |
__mul(table, value) | Operator mnożenia *. |
__div(table, value) | Operator dzielenia /. |
__idiv(table, value) | Operator podziału piętra //. |
__mod(table, value) | Operator modułu % . |
__pow(table, value) | Operator mnożenia ^. |
__tostring(table) | Wystrzeliwany, gdy ciąg jest wywoływany na stole. |
__metatable | Jeśli istnieje, blokuje metabelę, więc getmetatable() zwróci to zamiast metabeli, a setmetatable() będzie błędem. Wartość niefunkcjonalna. |
__eq(table, value) | The == równy operatorowi¹ |
__lt(table, value) | Operator mniej niż < mniej niż |
__le(table, value) | Operator <= ¹ |
__mode | Używany w słabych tabelach, deklarujący, czy klucze i/lub wartości tabeli są słabe.Zauważ, że odniesienia do instancji Roblox nigdy nie są słabe.Tabele, które przechowują takie odniesienia, nigdy nie zostaną zebrane do kosza. |
__len(table) | Wystrzeliwany, gdy operator długości # jest używany na obiekcie. |
__iter(table) | Używany do oznaczania niestandardowego iteratora przy użyciu ogólnej iteracji. |
Należy zauważyć, że pisząc funkcje dla metod arytmetycznych lub relacyjnych metametodów, dwa parametry funkcji są zamienialne między tablicą, która uruchomiła metametodę, a inną wartością.Na przykład, gdy wykonuje się operacje wektorowe z dzieleniem skalarów nie jest komutatywne.Dlatego jeśli pisałeś metody meta dla własnej klasy vector2, chciałbyś uważać, aby uwzględnić każdy scenariusz.
local vector2 = {__type = "vector2"}
local mt = {__index = vector2}
function mt.__div(a, b)
if type(a) == "number" then
-- a jest skalarnym, b jest wektorem
local scalar, vector = a, b
return vector2.new(scalar / vector.x, scalar / vector.y)
elseif type(b) == "number" then
-- a jest wektorem, b jest skalarnym
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
-- zarówno a jak i b są wektorami
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
Użycie
Istnieje wiele sposobów korzystania z metatablic, na przykład metametoda __unm sprawia, że stół staje się negatywny:
local metatable = {
__unm = function(t) -- __unm jest dla operatora jednoznacznego
local negated = {}
for key, value in t do
negated[key] = -value -- zaprzecz wszystkim wartościom w tej tabeli
end
return negated -- wróć do tabeli
end
}
local table1 = setmetatable({10, 11, 12}, metatable)
print(table.concat(-table1, "; ")) --> -10; -11; -12
Oto interesujący sposób deklarowania rzeczy za pomocą __index :
local metatable = {__index = {x = 1}}local t = setmetatable({}, metatable)print(t.x) --> 1
__index został wypuszczony, gdy x został indeksowany w tabeli i nie znaleziono go.Luau następnie przeszukiwał tabelę __index w poszukiwaniu indeksu o nazwie x i, znajdując jeden, zwrócił to.
Teraz możesz łatwo to zrobić za pomocą prostej funkcji, ale jest o wiele więcej, skąd to się wzięło. Weź to na przykład:
local t = {10, 20, 30}print(t(5))
Zazwyczaj nie możesz wezwać tabeli, ale dzięki metatabelom możesz:
local metatable = {
__call = function(t, param)
local sum = {}
for i, value in ipairs(t) do
sum[i] = value + param -- Dodaj argument (5) do wartości, a następnie umieść go w nowej tabeli (t).
end
return unpack(sum) -- Zwróć poszczególne wartości tabeli
end
}
local t = setmetatable({10, 20, 30}, metatable)
print(t(5)) --> 15 25 35
Możesz też zrobić o wiele więcej, takich jak dodawanie tabel:
local table1 = {10, 11, 12}local table2 = {13, 14, 15}for k, v in table1 + table2 doprint(k, v)end
Wystąpi błąd mówiący, że próbujesz wykonać arytmetykę na tabeli, ale działa, gdy próbujesz to zrobić z metatablicą:
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
Gdy grasz z metatabelami, możesz napotkać pewne problemy.Jeśli musisz użyć metametody __index do tworzenia nowych wartości w tabeli, ale metatabela tej tabeli ma również metametodę __newindex, chcesz użyć wbudowanej funkcji Luau rawset(), aby ustawić wartość bez uruchamiania żadnych metametod.Weź następujący kod jako przykład tego, co się stanie, jeśli nie użyjesz tych funkcji.
local t = setmetatable({}, {
__index = function(self, i)
self[i] = i * 10
return self[i]
end,
__newindex = function(self, i, v)
-- Nie ustawiaj wartości do tabeli w normalny sposób
end
})
print(t[1]) -- Causes a stack overflow
Przekroczenia stosu zdarzają się, gdy próbujesz zawołać funkcję z samej siebie zbyt wiele razy.W funkcji __index powyżej, self[i] jest ustawiony na wartość, więc gdy dotrze do następnej linii, self[i] powinien istnieć i prawdopodobnie nie będzie wzywał metodę __index metametody.Problem polega na tym, że __newindex nie pozwala ci ustawić wartości.Jego obecność powstrzymuje dodawanie wartości do tabeli za pomocą standardowej metody t[i] = v.Aby przez to przejść, użyj funkcji rawset():
local t = setmetatable({}, {
__index = function(self, i)
rawset(self, i, i * 10)
return self[i]
end,
__newindex = function(self, i, v)
-- Nie ustawiaj wartości do tabeli w normalny sposób
end
})
print(t[1]) --> 10
Użyj ustawionego typu danych
A zestaw to kolekcja przedmiotów bez kolejności i bez powtórnych elementów.Przedmiotem jest albo jest lub nie jest zawarty w ustawiać.Używając metatablic, możesz konstruować i manipulować zestawami w skryptach Luau.
Podstawowe metody
Poniższy kod zawiera podstawową funkcjonalność zestawu, umożliwiającą konstruowanie nowych zestawów, dodawanie i usuwanie przedmiotu, sprawdzanie, czy zestaw zawiera przedmiot, oraz wyświetlanie zawartości ustawiać.
local Set = {}
Set.__index = Set
-- Funkcja do konstruowania zestawu z opcjonalnej listy przedmiotów
function Set.new(items)
local newSet = {}
for key, value in items or {} do
newSet[value] = true
end
return setmetatable(newSet, Set)
end
-- Funkcja dodania przedmiotu do ustawiać
function Set:add(item)
self[item] = true
end
-- Funkcja usuwania przedmiotu z ustawiać
function Set:remove(item)
self[item] = nil
end
-- Funkcja sprawdzania, czy zestaw zawiera przedmiot
function Set:contains(item)
return self[item] == true
end
-- Funkcja wyświetlania zestawu jako listy oddzielonej przecinkami do debugowania
function Set:output()
local elems = {}
for key, value in self do
table.insert(elems, tostring(key))
end
print(table.concat(elems, ", "))
end
Utwórz ustawiać
Nowy zestaw można skonstruować, wywołując Set.new() z opcjonalną listą elementów do dodawać.
local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})
Zauważ, że z definicji zestaw nie ma pojęcia o kolejności.
Dodaj przedmiot
Dodanie przedmiotu do istniejącego zestawu można wykonać za pomocą metody Set:add().
local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})fruits:add("Mango")
Usuń przedmiot
Aby usunąć przedmiot z ustawiać, wezwij Set:remove() z nazwą przedmiotu.
local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})fruits:remove("Orange")
Sprawdź przedmiot
Aby sprawdzić, czy zestaw zawiera określony przedmiot, użyj Set:contains().
local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})local result1 = fruits:contains("Cherry")print(result1) -- prawdalocal result2 = fruits:contains("Watermelon")print(result2) -- false
Dodatkowe metody
Inne przydatne operacje mogą być wdrożone dla zestawów, umożliwiając porównanie przedmiotów między zestawami, połączenie zestawów lub odjęcie jednego zestawu od drugiego.
Intersekcja
Rozważając zestawy jako diagramy Venn, możesz uzyskać przełamanie dwóch zestawów w następujący sposób, oznaczając przedmioty, które pojawiają się w obu zestawach.
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
Związek
Możesz uzyskać unię z dwóch zestawów za pomocą następującej funkcji, co oznacza zbiór przedmiotów w obu zestawach bez powtórzeń.Zauważ, że ta funkcja wykorzystuje metodę metatable __add do zapewnienia dodatkowego skrótu 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() -- Brzoskwinia, Limonka, Jabłko, Wiśnia, Cytryna, Mango
Odejmowanie
Możesz usunąć wszystkie przedmioty w jednym zestawie z przedmiotami w innym zestawie za pomocą następującej funkcji.Podobnie do funkcji powyżej, wykorzystuje metodę metatable __sub, aby zapewnić skrót odliczania 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