Les métatables permettent aux tables de devenir plus puissantes que jamais. Ils sont attachés aux données et contiennent des valeurs appelées métamethodes. Les métamethodes sont déclenchées lorsque certaines actions sont utilisées avec le datum qu'ils sont attachés.
Consider le code suivant :
local list = {1, 2}print(list[3])
Vous pourriez attendre que ce code recherche dans la liste pour le troisième index dans la liste, trouve rien et renvoie zéro. Cela n'est pas correct, cependant. Ce qui se produit en fait est que le code recherche dans la liste pour le troisième index, trouve rien et puis vérifie si il y a une table métrique jointe à la table, renvoyant zéro si il n'y en a pas une.
Manipulation des métatables
Les deux fonctions principales pour ajouter et trouver une table, sont setmetatable et getmetatable
local x = {}local metaTable = {} -- Les metaTables sont des tables, aussi !setmetatable(x, metaTable) -- Donnez à x une table de métier appelée MetaTable !print(getmetatable(x)) --> table: [hexadecimal memory address]
La fonction setmetatable renvoie également la table que vous définissez le mettablestable, donc ces deux scripts font la même chose :
local x = {}setmetatable(x, {})
local x = setmetatable({}, {})
Métaméthodes
Les métaméthodes sont les fonctions qui sont stockées dans une métitable. Ils peuvent aller d'appeler une table, d'ajouter une table, voire même de diviser des tables. Voici la liste des métaméthodes disponibles :
Méthode | Description |
---|---|
__index(table, index) | Se déclenche lorsque la table[index] est indexée, si la table[index] est nulle. Peut également être défini sur une table, dans laquelle la table sera indexée. |
__newindex(table, index, valeur) | Se déclenche lorsque la table[index] essaie d'être définie (table[index] = valeur), si la table[index] est nulle. Peut également être défini sur une table, dans le cas où cette table sera indexée. |
__call(table, ...) | Se déclenche lorsque la table est appelée comme une fonction, ... sont les arguments qui ont été passés. |
__concat(table, value) | Se déclenche lorsque le .. opérateur de concatenation est utilisé sur la table. |
__unm(table) | Se déclenche lorsque l'opérateur unaire est utilisé sur la table. |
__add(table, value) | L'opérateur +. |
__sub(table, valeur) | Le - opérateur de soustraction. |
__mul(table, valeur) | L'opérateur de * multiplication. |
__div(table, valeur) | L'opérateur / division. |
__idiv(table, valeur) | L'opérateur // de division du sol. |
__mod(table, valeur) | L'opérateur modulaire % |
__pow(table, valeur) | L'opérateur ^ à l'exponentiation. |
__tostring(table) | Fired when tostring est appelé sur la table. |
__métable | Si présent, verrouille la table de métier pour obtenir métier obtenir en retour de la table de métier et de la table de métier, ce qui se produira en cas d'erreur. Non valeur de fonction. |
__eq(table, valeur) | Le == correspondant à l'opérateur¹ |
__lt(table, valeur) | Le < moins que l'opérateur¹ |
__le(table, valeur) | L'opérateur « <&%ri_tab%&> » |
__mode | Utilisé dans les tables faibles, déclarant si les clés et/ou les valeurs d'une table sont faibles. Remarque : Les références à des instances Roblox ne sont jamais faibles. Les tables qui contiennent de telles références ne seront jamais collectées. |
__gc(table) | Fired when the table is garbage-collected. Remarque : Sur Roblox, ce métaméthode est désactivé. |
__len(table) | Tiré lorsque l'opérateur de longueur # est utilisé sur l'objet. |
__iter(table) | Utilisé pour définir un itérateur personnalisé lors de l'utilisation d'une itération générale. |
Il doit être noté que lorsque vous écrivez des fonctions pour les métaméthodes arithmétiques ou relationnelles, les deux paramètres de fonction sont interchangeables entre la table qui a exécuté la métaméthode et l'autre valeur. Par exemple, lorsque vous écrivez des opérations de vécteur avec une division de scalaire n'est pas commutative. Donc, si vous écrivez des métaméthodes pour votre propre classe vecteur2, vous voudrez être prudent pour tenir compte de chaque scénario.
local vector2 = {__type = "vector2"}
local mt = {__index = vector2}
function mt.__div(a, b)
if type(a) == "number" then
-- a est un scalaire, b est un force vectorielle
local scalar, vector = a, b
return vector2.new(scalar / vector.x, scalar / vector.y)
elseif type(b) == "number" then
-- a est un force vectorielle, b est 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
-- les uns et les autres sont des vécteurs
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
Utiliser les métatables
Il existe de nombreuses façons d'utiliser les métatables, par exemple la __unm métaméthode (pour rendre une table négative) :
local metatable = {
__unm = function(t) -- __unm est pour l'opérateur unaire
local negated = {}
for key, value in t do
negated[key] = -value -- négatif toutes les valeurs dans cette table
end
return negated -- renvoyer la table
end
}
local table1 = setmetatable({10, 11, 12}, metatable)
print(table.concat(-table1, "; ")) --> -10; -11; -12
Voici une façon intéressante de déclarer des choses à l'aide de __index :
local metatable = {__index = {x = 1}}local t = setmetatable({}, metatable)print(t.x) --> 1
__index a été lancé lorsque x a été indexé dans le tableau et n'a pas été trouvé. Lua a ensuite recherché dans la table __index pour un index nommé x, et en trouvant un, il a renvoyé que.
Maintenant, vous pouvez facilement le faire avec une fonction simple, mais il y a beaucoup plus d'où cela vient. Prenez cet exemple :
local t = {10, 20, 30}print(t(5))
Maintenant, vous ne pouvez évidemment pas appeler une table. C'est juste fou, mais (surprise, surprise !) avec les métatables, vous pouvez le faire.
local metatable = {
__call = function(t, param)
local sum = {}
for i, value in ipairs(t) do
sum[i] = value + param -- Ajoutez l'argument (5) à la valeur, puis placez-le dans la nouvelle table (t).
end
return unpack(sum) -- Renvoie les valeurs de table individuelles
end
}
local t = setmetatable({10, 20, 30}, metatable)
print(t(5)) --> 15 25 35
Vous pouvez en faire beaucoup plus aussi, comme ajouter des tables !
local table1 = {10, 11, 12}local table2 = {13, 14, 15}for k, v in table1 + table2 doprint(k, v)end
Ceci se produira en échouant en disant que vous essayez d'effectuer de l'arithmétique sur une table. Essayons-le avec une table métrique.
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
Utiliser les cas
Maintenant, toutes ces exemples peuvent être implémentés comme une fonction simple, mais vous pouvez faire beaucoup plus que cela. Essayons un programme simple qui mémorisera un nombre lorsqu'un problème de mathématiques potentiellement lent sera mis dedans.
Pour celui-ci, nous allons utiliser la métaméthode __index pour le rendre simple :
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]) -- Ce sera lent car c'est la première fois à utiliser ce nombre, donc il doit exécuter la fonction de mathématiques.
print(t[2]) -- sera lent car c'est la première fois à utiliser ce nombre.
print(t[1]) -- will be fast because it's just grabbing the number from the table.
Rawset, Rawget, Rawequal
Lorsque vous jouez avec des métables, vous pouvez rencontrer quelques problèmes. Qu'est-ce qui se passe quand vous devez utiliser la métaméthode __index pour créer de nouvelles valeurs dans une table, mais que la métaméthode __newindex de cette table a également un métaméthode __newindex
local t = setmetatable({}, {
__index = function(self, i)
self[i] = i * 10 -- juste comme un exemple
return self[i]
end,
__newindex = function(self, i, v)
--ne faites rien parce que nous ne voulons pas que vous configuriez les valeurs à la table de la manière normale
end
})
print(t[1]) -- Causes a C-Stack overflow
Maintenant, pourquoi cela causerait un débordement de pile? Les débordements de pile se produisent lorsque vous essayez d'appeler une fonction à partir de lui trop de fois, mais qu'est-ce qui causerait que cela se produise? Dans la fonction __index, nous avons défini self[i] à une valeur, donc lorsqu'il arrive à la prochaine
Le problème est que __newindex ne nous permet pas de définir la valeur. Sa présence arrête les valeurs d'être ajoutées à la table avec la méthode standard t[i] = v. Pour y parvenir, vous utilisez la fonction rawset.
local t = setmetatable({}, {
__index = function(self, i)
rawset(self, i, i * 10)
return self[i]
end,
__newindex = function(self, i, v)
--ne faites rien parce que nous ne voulons pas que vous configuriez les valeurs à la table de la manière normale
end
})
print(t[1]) -- prints 10
Utiliser le type de données de sélection
Un set est une collection d'objets sans ordre et sans éléments en double. Un objet est soit est soit n'est pas contenu dans un configurer. En utilisant des métatables, vous pouvez construire et manipuler des 5>sets5> dans des scripts Lua.
Méthodes de base
Le code suivant inclut des fonctions de base, vous permettant de construire de nouveaux ensembles, d'ajouter et de supprimer un item, de vérifier si un élément contient un itemet d'afficher le contenu d'un configurer.
local Set = {}
Set.__index = Set
-- Fonction pour construire un ensemble à partir d'une liste facultative d'objets
function Set.new(items)
local newSet = {}
for key, value in items or {} do
newSet[value] = true
end
return setmetatable(newSet, Set)
end
-- Fonction à ajouter un élément à un configurer
function Set:add(item)
self[item] = true
end
-- Fonction pour supprimer un élément d'un configurer
function Set:remove(item)
self[item] = nil
end
-- Fonction pour vérifier si un ensemble contient un item
function Set:contains(item)
return self[item] == true
end
-- Fonction d'Output de set en tant que liste comma-delimited pour déboguer
function Set:output()
local elems = {}
for key, value in self do
table.insert(elems, tostring(key))
end
print(table.concat(elems, ", "))
end
Créer un ensemble
Un nouveau ensemble peut être construit en appelant Set.new() avec un ensemble d'objets facultatifs à ajouter.
local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})
Notez que par définition, un ensemble n'a pas de concept d'ordre.
Ajouter un article
L'ajout d'un élément à un ensemble existant peut être effectué via la méthode Set:add().
local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})fruits:add("Mango")
Supprimer l'élément
Pour supprimer un élément d'un configurer, appelez Set:remove() avec le nom de l'élément.
local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})fruits:remove("Orange")
Rechercher un élément
Pour vérifier si un ensemble contient un itemspécifique, utilisez Set:contains() .
local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})local result1 = fruits:contains("Cherry")print(result1) -- vrailocal result2 = fruits:contains("Watermelon")print(result2) -- false
Méthodes supplémentaires
D'autres opérations utiles peuvent être implémentées pour les ensembles, vous permettant de comparer des éléments entre des ensembles, de combiner des ensembles ou de soustraire un élément d'un autre.
Intersection
Lorsque vous envisagez les ensembles comme des diodes Venn, vous pouvez obtenir l'intersection deux ensembles comme suivant, ce qui signifie que les éléments qui apparaissent dans les deux ensembles.
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
Union
Vous pouvez obtenir l'union de deux ensembles avec la fonction suivante, ce qui signifie une collection des éléments dans les deux ensembles sans dupliqués. Notez que cette fonction utilise la méthode __add pour fournir une raccourci d'ajout de __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() -- Pêche, citron vert, pomme, cerise, citron, mangue
Soustraction
Vous pouvez supprimer tous les éléments dans un ensemble à partir des éléments dans un autre ensemble via la fonction suivante. Semblable à la fonction ci-dessus, cela utilise la méthode __sub pour fournir un raccourci de soustraction 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