[Notas de estudio de Lua] Lua Advanced - Tabla (3) Metatabla

Insertar descripción de la imagen aquí
Continuar desde arriba


metatabla

P: ¿Por qué utilizar metatablas?

R: En Lua, a menudo se requieren operaciones entre tablas. La metatabla proporciona algunos metamétodos. Puede lograr las funciones deseadas personalizando los metamétodos, lo que equivale a brindarle una serie de métodos en métodos orientados a objetos para que los sobrecargue.

Utilice el siguiente código para configurar la metatabla:

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

Normalmente las operaciones en la metatabla se dividen en tres pasos:

  1. Subtabla de operaciones
  2. Comprobar si hay una metatabla
  3. Si hay una metatabla, verifique si hay un metamétodo correspondiente, si no hay un metamétodo, regrese al procesamiento original de la operación correspondiente. Si hay un metamétodo correspondiente, ejecute el metamétodo.

Los índices de metamétodos generalmente comienzan con "__" (dos guiones bajos, creo que esto se debe a que el nombre de variables estáticas privadas a menudo comienza con un guión bajo), como en el siguiente ejemplo:

__tostring

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

输出:
123

El ejemplo anterior es equivalente a usar un metamétodo para sobrecargar la función de impresión.

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

输出:
233

En el ejemplo anterior, incluso si no especificamos los parámetros de entrada del metamétodo, debido a la asociación entre la subtabla y la metatabla, el metamétodo pasará automáticamente la subtabla al metamétodo como parámetro.


__call

Definamos otro __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

Después de definir __callel método meta, lo usamos table(index)y descubrimos que __callel método meta imprimió 123, 456 y llamó, 123 es el elemento de la subtabla y 456 es el __tostringresultado de retorno. Esto significa que __callel método llama a la subtabla como parámetro a. Y al igual que nuestro ejemplo de impresión anterior, print(a)el método también llama __tostringal metamétodo. Obviamente, el parámetro 2 in table(2)se ignora.

Ahora podemos determinar una regla básica: cuando usamos __tostringel __callmetamétodo de suma, el primer parámetro que definimos debe ser la propia tabla de subtabla, solo definiendo más parámetros podremos recibir otros 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 el índice del nombre en la tabla, pero por supuesto no existe. El resultado de salida es nulo. ¿
Puede la metatabla tener este índice? La respuesta es no, porque todavía estamos buscando la tabla1:

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

输出:
nil

Ahora definimos un metamétodo __indexque apuntará a otra tabla de consulta.

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

输出:
2

Todo el proceso de búsqueda es en realidad:

  1. Subtabla de consulta
  2. Error en la consulta de subtabla, consulte la metatabla para ver si hay un __indexmetamétodo
  3. Si es así, consulte __indexla tabla señalada por el metamétodo.

Sin embargo, existe otra situación en la que se recomienda no __indexdefinir el metamétodo dentro de la metatabla:

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


输出:
nil   --不明觉厉

También puedes usar muñecas matrioskas.

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

Mira 4 ejemplos:

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

¿Es raro? De hecho, es fácil de entender. Esto se debe al __newindexmétodo meta. Su función es en realidad agregar los elementos recién agregados de la subtabla a __newindexla tabla puntiaguda sin modificar la subtabla (por supuesto, __newindextambién se puede usar como muñeca nido):

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

输出:
nil
1


metamétodo del 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

Descubrimos que cuando se utiliza el metamétodo del operador, el primer parámetro ya no es la subtabla vinculada de forma predeterminada. En cambio, al metamétodo se le asignan valores en secuencia de acuerdo con las variables izquierda y derecha del operador. E independientemente de si la metatabla está configurada como sustraendo o minuendo, el resultado final permanece sin cambios. Hay muchos métodos de operador, por lo que no los enumeraré en detalle aquí. Copié una tabla directamente del tutorial para principiantes:

metamétodo describir
__agregar Operador correspondiente '+'
__sub Operador correspondiente '-'
__mul Operador correspondiente '*'
__div Operador correspondiente '/'
__modificación Operador correspondiente '%'
__unm Operador correspondiente '-'
__concat El operador correspondiente '..'
__poder Operador correspondiente '^'
__eq. Operador correspondiente '=='
__lt Operador correspondiente '<'
__el Operador correspondiente '<='

Además de las operaciones matemáticas normales, también necesitamos hablar sobre los metamétodos más especiales de juicio lógico (las columnas en negrita en la tabla anterior). Estos metamétodos requieren que ambos lados del operador estén vinculados al mismo Yuan. mesa. La razón es que el metamétodo no proporciona un símbolo mayor que. El metamétodo para calcular a>b es en realidad equivalente al método original para calcular b<a. Esto requiere que b también esté vinculado a la metatabla. Por lo tanto, , Las operaciones lógicas requieren que ambas partes estén vinculadas al mismo elemento Tabla, veamos el siguiente ejemplo:

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 encontrar que hay un problema con el juicio lógico de igualdad anterior: la primera impresión es falsa y la segunda impresión es verdadera.

En la primera impresión, solo table1se vincula la metatabla, pero table2no la metatabla, porque return falsedurante las operaciones lógicas, tanto el lado izquierdo como el derecho deben estar vinculados a la misma metatabla.

Y en la segunda impresión, se imprimió true, aunque fuera diferente table1del table2contenido. Debido a que este es nuestro metamétodo personalizado, es lo que devolvemos de forma predeterminada true.

Algunos ejemplos. Considere los resultados de salida de los siguientes ejemplos:

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

En el ejemplo anterior, utilizamos __eqel método meta para determinar agesi los valores del índice son iguales, lo que obviamente es diferente, por lo que esfalse

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

En el ejemplo anterior, usamos el método meta para determinar namesi los valores del índice son los mismos. La Tabla1 no tiene nameun índice. El valor predeterminado es nil, la Tabla2 sí name = nil, por lo que estrue

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)

输出:
栈溢出

En el ejemplo anterior, hacemos un juicio dentro del metamétodo t1==t2, y al realizar el juicio, es equivalente a llamar __eqal metamétodo nuevamente, por lo que se repetirá infinitamente y eventualmente provocará un desbordamiento de la pila.


Otras operaciones de metatabla

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

Supongo que te gusta

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