[Notas de Estudo Lua] Lua Avançado - Tabela (3) Metatabela

Insira a descrição da imagem aqui
Continuar de cima


metatabela

P: Por que usar metatabelas?

R: Em Lua, muitas vezes são necessárias operações entre tabelas. A metatabela fornece alguns metamétodos. Você pode obter as funções desejadas personalizando metamétodos, o que equivale a fornecer uma série de métodos em métodos orientados a objetos para você sobrecarregar.

Use o seguinte código para configurar a metatabela:

meta={
    
    }
table={
    
    }
setmetatable(table,meta) -- 第一个元素为子表,第二个元素为元表

Normalmente as operações na metatabela são divididas em três etapas:

  1. Subtabela de operação
  2. Verifique se existe uma metatabela
  3. Se houver uma meta-tabela, verifique se existe um meta-método correspondente. Se não houver um meta-método, retorne ao processamento original da operação correspondente. Se houver um metamétodo correspondente, execute o metamétodo.

Os índices de metamétodos geralmente começam com "__" (dois sublinhados, acho que isso ocorre porque nomear variáveis ​​estáticas privadas geralmente começa com um sublinhado), como no exemplo a seguir:

__tostring

meta = {
    
    }
table={
    
    }
setmetatable(table, meta)
print(table)
输出:
table: 00ABA2A8
meta = {
    
    
    __tostring = function ()  <--注意两个下划线
        return "123"
    end
}
table={
    
    }
setmetatable(table, meta)
print(table)

输出:
123

O exemplo acima é equivalente a usar um método meta para sobrecarregar a função de impressão

meta = {
    
    
    __tostring = function (t)
        return t.name
    end
}
table={
    
    name = "233"}
setmetatable(table, meta)
print(table)

输出:
233

No exemplo acima, mesmo que não especifiquemos os parâmetros de entrada do metamétodo, devido à associação entre a subtabela e a metatabela, o metamétodo passará automaticamente a subtabela para o metamétodo como parâmetro.


__call

Vamos definir outro __callmetamétodo

meta = {
    
    
    __tostring = function ()
        return "456"
    end,
    __call = function(a)
        print(a.name)
        print(a)
        print("call")
    end
}
table={
    
    name = "123"}
setmetatable(table, meta)
table(2)  --无论table(x)中给出的x为几,结果都是一样的

输出:
123
456
call

Depois de definir __callo metamétodo, nós o usamos table(index)e descobrimos que __callo metamétodo imprimiu 123, 456 e chamada.123 é o elemento da subtabela e 456 é o __tostringresultado de retorno. Isso significa que __callo método chama a subtabela como parâmetro a. E como nosso exemplo de impressão acima, print(a)o método também chama __tostringo metamétodo. O parâmetro 2 in table(2)é obviamente ignorado.

Agora podemos determinar uma regra básica: ao usar __tostringo __callmetamétodo sum, o primeiro parâmetro que definimos deve ser a própria tabela da subtabela. Somente definindo mais parâmetros podemos receber outros parâmetros de entrada:

meta = {
    
    
    __tostring = function ()
        return "456"
    end,
    __call = function(a,b)
        print(a)
        print(b)
        print("call")
    end
}
table={
    
    name = "123"}
setmetatable(table, meta)
table(2)

输出:
456
2
call

__index

meta = {
    
    
}
table1 = {
    
     age = 1 }
setmetatable(table1, meta)
print(table1.name)

输出:
nil

Queremos encontrar o índice do nome na tabela, mas é claro que ele não existe. O resultado da saída é nulo.
A metatabela pode ter esse índice? A resposta é não, pois ainda estamos procurando a tabela1:

meta = {
    
    name =1}
table1 = {
    
     age = 1 }
setmetatable(table1, meta)
print(table1.name)

输出:
nil

Agora definimos um metamétodo __indexque apontará para outra tabela de consulta

meta = {
    
     name = 2 }
meta.__index = meta
table1 = {
    
     age = 1 }
setmetatable(table1, meta)
print(table1.name)

输出:
2

Todo o processo de pesquisa é na verdade:

  1. Subtabela de consulta
  2. Falha na consulta da subtabela, consulte a metatabela para ver se existe um __indexmetamétodo
  3. Se sim, consulte __indexa tabela apontada pelo meta-método

Porém, há outra situação em que é recomendado não __indexdefinir o metamétodo dentro da metatabela:

meta = {
    
    
    name = 2,
    __index = meta,
}
table1 = {
    
     age = 1 }
setmetatable(table1, meta)
print(table1.name)


输出:
nil   --不明觉厉

Você também pode usar bonecas matryoshka

meta = {
    
    
}
metaFather = {
    
    
    name =4,
}
table1 ={
    
    age =1}
meta.__index = meta  --让元表的index指向meta,子表找不到就去meta里找
metaFather.__index =metaFather --让元表的index指向metaFather,子表找不到就去metaFather里找
setmetatable(meta,metaFather)
setmetatable(table1, meta)
print(table1.name)

输出:i
4  --允许套娃

__newindex

Veja 4 exemplos:

meta = {
    
    }
table1 ={
    
    }
table1.age = 1
setmetatable(table1, meta)
print(table1.age)

输出:
1

meta = {
    
    }
table1 ={
    
    age = 1}
meta.__newindex = {
    
    }
setmetatable(table1, meta)
print(table1.age)

输出:
1

meta = {
    
    }
table1 ={
    
    }
meta.__newindex = {
    
    }
setmetatable(table1, meta)
table1.age = 1
print(table1.age)

输出:
nil

meta = {
    
    }
table1 ={
    
    }
meta.__newindex = {
    
    }
table1.age = 1
setmetatable(table1, meta)
print(table1.age)

输出:
1

É estranho? Na verdade, é fácil de entender. Isso se deve ao __newindexmétodo meta. Sua função é, na verdade, adicionar os elementos recém-adicionados da subtabela à __newindextabela apontada sem modificar a subtabela (é claro __newindexque também pode ser usado como uma boneca de nidificação):

meta = {
    
    }
table1 ={
    
    }
meta.__newindex = {
    
    }
setmetatable(table1, meta)
table1.age =1
print(table1.age)
print(meta.__newindex.age)

输出:
nil
1


metamétodo do operador

meta = {
    
    
    __sub = function (t1,t2)
        return t1.age - t2.age
    end
}
table1={
    
    age=1}
table2={
    
    age=2}
setmetatable(table1, meta)  --无论把元表设置给table1还是table2,结果都一样
print(table1 - table2)

输出:
-1

Descobrimos que ao usar o metamétodo do operador, o primeiro parâmetro não é mais a subtabela vinculada por padrão. Em vez disso, o metamétodo recebe valores em sequência de acordo com as variáveis ​​esquerda e direita do operador. E independentemente de a metatabela estar definida como subtraendo ou minuendo, o resultado final permanece inalterado. Existem muitos métodos de operador, então não vou listá-los em detalhes aqui. Copiei uma tabela diretamente do tutorial para iniciantes:

metamétodo descrever
__adicionar Operador correspondente '+'
__sub Operador correspondente '-'
__mul Operador correspondente '*'
__div Operador correspondente '/'
__mod Operador correspondente '%'
__unm Operador correspondente '-'
__concat O operador correspondente '..'
__Pancada Operador correspondente '^'
__eq Operador correspondente '=='
__lt Operador correspondente '<'
__o Operador correspondente '<='

Além das operações matemáticas normais, também precisamos falar sobre os metamétodos mais especiais de julgamento lógico (as colunas em negrito na tabela acima).Esses metamétodos exigem que ambos os lados do operador estejam vinculados ao mesmo Yuan mesa. A razão é que o metamétodo não fornece um símbolo maior que. O metamétodo de calcular a>b é na verdade equivalente ao método original de calcular b<a. Isso exige que b também esteja vinculado à metatabela. Portanto, , as operações lógicas exigem que ambos os lados da operação estejam vinculados ao mesmo elemento. Tabela, vejamos o seguinte exemplo:

meta = {
    
    
    __eq= function (t1,t2)
        return true
    end,
}
table1 = {
    
     age = 1 }
table2 = {
    
     name = nil }
setmetatable(table1, meta)
print(table1 == table2)
setmetatable(table2, meta)
print(table1 == table2)

输出:
false
true

Podemos descobrir que há um problema com o julgamento lógico de igualdade acima: a primeira impressão é falsa e a segunda impressão é verdadeira.

Na primeira impressão,apenas table1a metatabela está ligada,mas table2não a metatabela.A razão return falseé porque durante as operações lógicas,tanto os lados esquerdo como o direito precisam de ser ligados à mesma metatabela.

E na segunda tiragem foi impresso true, mesmo que fosse diferente table1do table2conteúdo. Como este é nosso metamétodo personalizado, é o que retornamos por padrão true.

Alguns exemplos. Considere os resultados de saída dos seguintes exemplos:

meta = {
    
    
    __eq= function (t1,t2)
        if t1.age == t2.age then
            return true
        end
    end,
}
table1 = {
    
     age = 1 }
table2 = {
    
     age = 2 }
setmetatable(table1, meta)
setmetatable(table2, meta)
print(table1 == table2)

输出:
false

No exemplo acima, usamos __eqo método meta para determinar agese os valores do índice são iguais, o que é obviamente diferente, então éfalse

meta = {
    
    
    __eq= function (t1,t2)
        if t1.name == t2.name then
            return true
        end
    end,
}
table1 = {
    
     age = 1 }
table2 = {
    
     name = nil }
setmetatable(table1, meta)
setmetatable(table2, meta)
print(table1 == table2)

输出:
true

No exemplo acima, usamos o método meta para determinar namese os valores do índice são iguais. Tabela1 não tem nameíndice. O padrão é nil, tabela2 tem name = nil, então étrue

meta = {
    
    
    __eq= function (t1,t2)
        if t1 == t2 then
            return true
        end
    end,
}
table1 = {
    
     age = 1 }
table2 = {
    
     name = nil }
setmetatable(table1, meta)
setmetatable(table2, meta)
print(table1 == table2)

输出:
栈溢出

No exemplo acima, fazemos um julgamento dentro do meta-método t1==t2e, ao fazer o julgamento, é equivalente a chamar __eqo meta-método novamente, então ele fará um loop infinito e eventualmente levará ao estouro de pilha.


Outras operações metatabelas

print(getmetatable(table1)) --使用getmetatable方法获得table1的元表地址

输出:
table: 00BBA320
meta = {
    
     name = 2 }
meta.__index = meta
table1 = {
    
     age = 1 }
setmetatable(table1, meta)
print(rawget(table1,"nane"))  --rawget方法只查询子表,无视元表index

输出:
nil
meta = {
    
    }
table1 ={
    
    }
meta.__newindex = {
    
    }
setmetatable(table1, meta)
table1.age =1
print(table1.age)
rawset(table1, "age", 1) --rawset方法可以无视元表的newindex,直接修改子表
print(table1.age)

输出:
nil
1

Acho que você gosta

Origin blog.csdn.net/milu_ELK/article/details/131965788
Recomendado
Clasificación