Métatable Lua

Dans une table Lua, nous pouvons accéder à la clé correspondante pour obtenir la valeur, mais nous ne pouvons pas effectuer d'opérations sur deux tables (comme l'addition).

Par conséquent, Lua fournit des métatables, ce qui nous permet de modifier le comportement des tables. Chaque comportement est associé à une métaméthode correspondante.

Par exemple, en utilisant des métatables, nous pouvons définir comment Lua calcule l'opération d'addition a+b de deux tables.

Lorsque Lua essaie d'additionner deux tables, il vérifie d'abord si l'une d'elles possède une métatable, puis vérifie s'il existe un champ appelé__addSi trouvé, la valeur correspondante est appelée.__addDes champs comme ceux-ci ; leurs valeurs correspondantes (souvent une fonction ou une table) sont les « métaméthodes ».

Il existe deux fonctions très importantes pour gérer les métatables :

  • setmetatable(table,metatable):Définit la métatable pour la table spécifiée. Si la métatable contient une clé __metatable, setmetatable échouera.
  • getmetatable(table):Renvoie la métatable de l'objet.

L'exemple suivant montre comment définir une métatable pour une table spécifiée :

mytable = {}                          -- Table ordinaire
mymetatable = {}                      -- Métatable
setmetatable(mytable,mymetatable)     -- Définir mymetatable comme la métatable de mytable

Le code ci-dessus peut également s'écrire sur une seule ligne :

mytable = setmetatable({},{})

Ce qui suit renvoie la métatable d'un objet :

getmetatable(mytable)                 -- 这会返回 mymetatable

métaméthode __index

C'est la clé la plus couramment utilisée dans une métatable.

Lorsque vous accédez à une table par clé, si la clé n'a pas de valeur, Lua recherchera la clé __index dans la métatable de la table (en supposant qu'il y ait une métatable). Si __index contient une table, Lua recherche la clé correspondante dans cette table.

Nous pouvons utiliser la commande lua pour entrer en mode interactif et vérifier :

$ lua
Lua 5.3.0 Copyright (C) 1994-2015 Lua.org, PUC-Rio
> other = { foo = 3 }
> t = setmetatable({}, { __index = other })
> t.foo
3
> t.bar
nil

Si __index contient une fonction, Lua appellera cette fonction, en passant la table et la clé comme arguments à la fonction.

La métaméthode __index vérifie si un élément existe dans la table. S'il n'existe pas, le résultat renvoyé est nil ; s'il existe, le résultat est renvoyé par __index.

Exemple

mytable = setmetatable({key1 = "value1"}, {
  __index = function(mytable, key)
    if key == "key2" then
      return "metatablevalue"
    else
      return nil
    end
  end
})

print(mytable.key1,mytable.key2)

La sortie de l'exemple est :

value1    metatablevalue

Analyse de l'exemple :

  • La table mytable est assignée comme{key1 = "value1"}。

  • mytable a une métatable définie, et la métaméthode est __index.

  • Recherchez key1 dans la table mytable. S'il est trouvé, retournez l'élément ; sinon, continuez.

  • Recherchez key2 dans la table mytable. S'il est trouvé, retournez metatablevalue ; sinon, continuez.

  • Déterminez si la métatable possède une méthode __index. Si la méthode __index est une fonction, appelez cette fonction.

  • Dans la métaméthode, vérifiez si le paramètre de clé "key2" est passé (mytable.key2 est défini). Si le paramètre "key2" est passé, retournez "metatablevalue" ; sinon, retournez la valeur correspondante de mytable pour la clé.

Nous pouvons réécrire le code ci-dessus simplement comme suit :

mytable = setmetatable({key1 = "value1"}, { __index = { key2 = "metatablevalue" } })
print(mytable.key1,mytable.key2)

Résumé

Les règles lorsque Lua recherche un élément de table sont en fait les 3 étapes suivantes :

  • 1. Recherchez dans la table. Si trouvé, retournez l'élément ; sinon, continuez.
  • 2. Déterminez si la table possède une métatable. Si elle n'a pas de métatable, retournez nil ; si elle a une métatable, continuez.
  • 3. Déterminez si la métatable possède une méthode __index. Si la méthode __index est nil, retournez nil ; si la méthode __index est une table, répétez les étapes 1, 2 et 3 ; si la méthode __index est une fonction, retournez la valeur de retour de cette fonction.

Cette partie provient de l'auteur Huanzi : https://blog.csdn.net/xocoder/article/details/9028347


métaméthode __newindex

La métaméthode __newindex est utilisée pour mettre à jour les tables, tandis que __index sert à accéder aux tables.

Lorsque vous affectez une valeur à un index inexistant d'une table, l'interpréteur recherche la métaméthode __newindex : si elle existe, il appelle cette fonction sans effectuer l'affectation.

L'exemple suivant illustre l'application de la métaméthode __newindex :

Exemple

mymetatable = {}
mytable = setmetatable({key1 = "value1"}, { __newindex = mymetatable })

print(mytable.key1)

mytable.newkey = "new value 2"
print(mytable.newkey,mymetatable.newkey)

mytable.key1 = "new value 1"
print(mytable.key1,mymetatable.key1)

La sortie après l'exécution de l'exemple ci-dessus est :

value1
nil    新值2
新值1    nil

Dans l'exemple ci-dessus, la table a la métaméthode __newindex définie. Lors de l'affectation à une nouvelle clé d'index (newkey) (mytable.newkey = "new value 2"), la métaméthode est appelée et l'affectation n'est pas effectuée. Lors de l'affectation à une clé d'index existante (key1), l'affectation est effectuée et la métaméthode __newindex n'est pas appelée.

L'exemple suivant utilise la fonction rawset pour mettre à jour une table :

Exemple

mytable = setmetatable({key1 = "value1"}, {
    __newindex = function(mytable, key, value)
        rawset(mytable, key, "\""..value.."\"")
    end
})

mytable.key1 = "new value"
mytable.key2 = 4

print(mytable.key1,mytable.key2)

La sortie après l'exécution de l'exemple ci-dessus est :

new value    "4"

Ajout d'opérateurs aux tables

L'exemple suivant démontre l'addition de deux tables :

Exemple

-- Fonction personnalisée table_maxn pour calculer la valeur de clé maximale dans une table, c'est-à-dire renvoyer la valeur de clé maximale de la table
function table_maxn(t)
    local mn = 0
    for k, _ in pairs(t) do
        if type(k) == "number" and k > mn then
            mn = k
        end
    end
    return mn
end

-- Opération d'addition de deux tables
mytable = setmetatable({ 1, 2, 3 }, {
  __add = function(mytable, newtable)
    local max_key_mytable = table_maxn(mytable)
    for i = 1, table_maxn(newtable) do
      table.insert(mytable, max_key_mytable + i, newtable[i])
    end
    return mytable
  end
})

secondtable = {4, 5, 6}

mytable = mytable + secondtable

for k, v in ipairs(mytable) do
    print(k, v)
end

La sortie après l'exécution de l'exemple ci-dessus est :

1    1
2    2
3    3
4    4
5    5
6    6

La clé __add est incluse dans la métatable et effectue l'opération d'addition. La liste des opérations correspondantes dans la table est la suivante : (Remarque :__il y a deux traits de soulignement)

ModèleDescription
__addOpérateur correspondant '+'.
__subOpérateur correspondant '-'.
__mulOpérateur correspondant '*'.
__divOpérateur correspondant '/'.
__modOpérateur correspondant '%'.
__unmOpérateur correspondant '-'.
__concatOpérateur correspondant '..'.
__eqOpérateur correspondant '=='.
__ltOpérateur correspondant '<'.
__leOpérateur correspondant '<='.

Métaméthode __call

La métaméthode __call est appelée lorsque Lua appelle une valeur. L'exemple suivant montre comment calculer la somme des éléments d'une table :

Exemple

-- Calculer la valeur maximale dans la table ; table.maxn ne peut pas être utilisé dans les versions de Lua supérieures à 5.2
-- Fonction personnalisée table_maxn pour calculer la valeur de clé maximale dans une table, c'est-à-dire calculer le nombre d'éléments dans la table
function table_maxn(t)
    local mn = 0
    for k, v in pairs(t) do
        if mn < k then
            mn = k
        end
    end
    return mn
end

-- Définir la métaméthode __call
mytable = setmetatable({10}, {
  __call = function(mytable, newtable)
        sum = 0
        for i = 1, table_maxn(mytable) do
                sum = sum + mytable[i]
        end
    for i = 1, table_maxn(newtable) do
                sum = sum + newtable[i]
        end
        return sum
  end
})
newtable = {10,20,30}
print(mytable(newtable))

La sortie après l'exécution de l'exemple ci-dessus est :

70

métaméthode __tostring

La métaméthode __tostring est utilisée pour modifier le comportement de sortie d'une table. Dans l'exemple suivant, nous personnalisons le contenu de sortie d'une table :

Exemple

mytable = setmetatable({ 10, 20, 30 }, {
  __tostring = function(mytable)
    sum = 0
    for k, v in pairs(mytable) do
                sum = sum + v
        end
    return "La somme de tous les éléments de la table est " .. sum
  end
})
print(mytable)

La sortie après l'exécution de l'exemple ci-dessus est :

表所有元素的和为 60

Grâce à cet article, nous pouvons apprendre que les métatables peuvent efficacement simplifier les fonctionnalités de notre code, donc comprendre les métatables de Lua peut nous aider à écrire un code Lua plus simple et meilleur.

Autres extensions