English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Lua 元表(Metatable)

在 Lua table 中我们可以访问对应的key来得到value值,但是却无法对两个 table 进行操作。

因此 Lua 提供了元表(Metatable),允许我们改变table的行为,每个行为关联了对应的元方法。

例如,使用元表我们可以定义Lua如何计算两个table的相加操作a+b。

当Lua试图对两个表进行相加时,先检查两者之一是否有元表,之后检查是否有一个叫"__add"的字段,若找到,则调用对应的值。"__add"等即时字段,其对应的值(往往是一个函数或是table)就是"元方法"。

有两个很重要的函数来处理元表:

  • setmetatable(table,metatable): 对指定 table 设置元表(metatable),如果元表(metatable)中存在 __metatable 键值,setmetatable 会失败。

  • getmetatable(table): 返回对象的元表(metatable)。

以下示例演示了如何对指定的表设置元表:

mytable = {} -- 普通表 
mymetatable = {} -- 元表
setmetatable(mytable,mymetatable) -- 把 mymetatable 设为 mytable 的元表

Il codice sopra può anche essere scritto in una riga:}

mytable = setmetatable({},{})

Di seguito è riportato l'esempio di tabella meta restituita:

getmetatable(mytable)               -- Questo restituirà mymetatable

Metodo meta __index

Questa è la chiave più utilizzata nella tabella meta.

Quando accedi a una tabella tramite una chiave, se la chiave non ha un valore, Lua cerca la tabella meta (supponiamo che esista una tabella meta) con la chiave __index.

Possiamo usare il comando lua per entrare in modalità interattiva e vedere:

$ 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

Se __index contiene una funzione, Lua chiama quella funzione, passando la tabella e la chiave come parametri alla funzione.

Il metodo meta __index verifica l'esistenza dell'elemento nella tabella, se non esiste, il risultato è nil; se esiste, il risultato è restituito da __index.

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

Esempio di risultato di output:

value1  metatablevalue

Esempio di解析:

  • Assegna il valore a mytable: {key1 = "value1"}

  • mytable ha impostato la tabella meta, il metodo meta è __index.

  • Cerca key1 nella tabella mytable, se la trovi, restituisce quell'elemento, se non la trovi, continua.

  • Cerca key2 nella tabella mytable, se la trovi, restituisce metatablevalue, altrimenti continua.

  • Verifica se la tabella meta ha il metodo __index, se __index è una funzione, chiama quella funzione.

  • Controlla nel metodo meta se è stato passato il parametro chiave "key2" (mytable.key2 è stato impostato), se è stato passato il parametro "key2", restituisce "metatablevalue", altrimenti restituisce il valore della chiave corrispondente a mytable.

Possiamo scrivere semplicemente il codice sopra: }}

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

Conclusione

Le regole di ricerca di un elemento nella tabella di Lua sono in realtà tre passaggi come segue:

  • 1. Cerca nella tabella, se trova un elemento, restituisce quell'elemento, se non trova nulla, continua

  • 2. Verifica se la tabella ha una tabella meta, se non ce n'è, restituisce nil, se c'è, continua.

  • 3. Verifica se il metodo __index della tabella meta esiste, se __index è nil, restituisce nil; se __index è una tabella, ripeti i punti 1, 2, 3; se __index è una funzione, restituisce il valore di ritorno della funzione.

Questa parte del contenuto è stata fornita dall'autore寰子: https://blog.csdn.net/xocoder/article/details/9028347

Metodo meta __newindex

Il metodo meta __newindex viene utilizzato per aggiornare la tabella, mentre __index viene utilizzato per accedere alla tabella.

Quando si assegna un valore a un indice mancante nella tabella, l'interprete cerca il metodo meta __newindex: se esiste, chiama questa funzione senza effettuare l'operazione di assegnazione.

L'esempio seguente dimostra l'applicazione del metodo meta __newindex:

mymetatable = {}
mytable = setmetatable({key1 = "value1"}, {__newindex = mymetatable})
print(mytable.key1)
mytable.newkey = "valore nuovo2"
print(mytable.newkey, mymetatable.newkey)
mytable.key1 = "valore nuovo1"
print(mytable.key1, mymetatable.key1)

I risultati di esecuzione degli esempi sopra sono:

value1
nil           valore nuovo2
valore nuovo1           nil

Nell'esempio riportato, la tabella ha impostato il metodo meta __newindex, quando si assegna un nuovo chiave di indice (newkey) (mytable.newkey = "valore nuovo2"), viene chiamato il metodo meta, senza effettuare l'assegnazione.

Ecco un esempio che utilizza la funzione rawset per aggiornare la tabella:

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)

I risultati di esecuzione degli esempi sopra sono:

Nuovo valore  "4"

Aggiungi operatore alla tabella

Ecco un esempio che dimostra l'operazione di somma tra due tabelle:

-- La funzione massima della tabella, table.maxn, non è più disponibile nelle versioni superiori al Lua 5.2
-- Funzione massima di calcolo personalizzata per il numero di elementi della tabella, table_maxn
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
-- Operazione di somma tra due tabelle
mytable = setmetatable({ 1, 2, 3 }, {
  __add = function(mytable, newtable)
    for i = 1, table_maxn(newtable) do
      table.insert(mytable, table_maxn(mytable)+1,newtable[i])
    end
    return mytable
  end
)
secondtable = {4,5,6}
mytable = mytable + secondtable
        for k,v in ipairs(mytable) do
print(k,v)
end

I risultati di esecuzione degli esempi sopra sono:

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

__add chiave inclusa nella tabella meta e sommata. Elenco delle operazioni corrispondenti nella tabella: (Attenzione:__è due underscore)

ModoDescrizione
__addOperatore corrispondente '+'.
__subOperatore corrispondente '-'.
__mulOperatore corrispondente '*'.
__divOperatore corrispondente '/'.
__modOperatore corrispondente '%'.
__unmOperatore corrispondente '-'.
__concatOperatore corrispondente '..'.
__eqOperatore corrispondente '=='.
__ltOperatore corrispondente '<'.
__leOperatore corrispondente '<='.

__call metodo meta

__call metodo meta chiamato quando Lua chiama un valore. Ecco un esempio che dimostra come calcolare la somma degli elementi della tabella:

-- La funzione massima della tabella, table.maxn, non è più disponibile nelle versioni superiori al Lua 5.2
-- Funzione massima di calcolo personalizzata per il numero di elementi della tabella, table_maxn
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
-- Definire il metodo meta __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))

I risultati di esecuzione degli esempi sopra sono:

70

Metodo meta __tostring

Il metodo meta __tostring viene utilizzato per modificare il comportamento di output della tabella. Nei seguenti esempi, abbiamo personalizzato il contenuto di output della tabella:

mytable = setmetatable({10, 20, 30}, {
  __tostring = function(mytable)
    sum = 0
    for k, v in pairs(mytable) do
                sum = sum + v
        end
    return "La somma di tutti gli elementi della tabella è " .. sum
  end
)
print(mytable)

I risultati di esecuzione degli esempi sopra sono:

La somma di tutti gli elementi della tabella è 60

Da questo articolo possiamo sapere che il metatable può semplificare notevolmente le funzionalità del nostro codice, quindi conoscere il metatable di Lua ci permette di scrivere codice Lua più semplice ed eccellente.