Continuar desde arriba
Directorio de artículos
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:
- Subtabla de operaciones
- Comprobar si hay una metatabla
- 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 __call
metamé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 __call
el método meta, lo usamos table(index)
y descubrimos que __call
el método meta imprimió 123, 456 y llamó, 123 es el elemento de la subtabla y 456 es el __tostring
resultado de retorno. Esto significa que __call
el 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 __tostring
al metamétodo. Obviamente, el parámetro 2 in table(2)
se ignora.
Ahora podemos determinar una regla básica: cuando usamos __tostring
el __call
metamé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 __index
que 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:
- Subtabla de consulta
- Error en la consulta de subtabla, consulte la metatabla para ver si hay un
__index
metamétodo - Si es así, consulte
__index
la tabla señalada por el metamétodo.
Sin embargo, existe otra situación en la que se recomienda no __index
definir 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 __newindex
método meta. Su función es en realidad agregar los elementos recién agregados de la subtabla a __newindex
la tabla puntiaguda sin modificar la subtabla (por supuesto, __newindex
tambié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 table1
se vincula la metatabla, pero table2
no la metatabla, porque return false
durante 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 table1
del table2
contenido. 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 __eq
el método meta para determinar age
si 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 name
si los valores del índice son los mismos. La Tabla1 no tiene name
un í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 __eq
al 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