metatables:有点类似C++中的父类,当对tables进行一些操作时,会去到它的metatables中找相应的域来实现。metatables是一个普通的表,任何一个表都可以成为另一个表的metatables,也可以是自己的metatables。
metamethods:metatables中的域。lua中有一些核心的metamethods,一旦在使用tables的时候满足触发条件,就会跑到metatables中去调用。
lua中支持tables中的域进行算数运算,但是如果想要对tables进行算数运算就需要两张表至少有一张的metatables中包含算数运算相关的域。
“+” -> _add; “-” -> _sub; “*” -> _mul; “/” -> _div; “-” -> _unm; “” ->_pow(幂);
先了解一下这个触发关系。
当两张表相加的时候会先到第一张表的metatables中寻找是否有_add的域,如果有就按照第一张表的metatables运行,如果没有就在第二张表中寻找,如果还是没有就报错。
我们通过以下这个例子来深入了解。
Set = {}
Set.mt = {}
function Set.New(t)
local set = {}
setmetatable(set,Set.mt)
for _,i in ipairs(t) do set[i] = true end
return set
end
function Set.union(a,b)
local res = Set.New{}
for k in pairs(a) do res[k] = true end
for k in pairs(b) do res[k] = true end
return res
end
function Set.intersection(a,b)
local res = Set.New{}
for k in pairs(a) do
res[k] = b[k]
end
return res
end
function Set.tostring(set)
local s = "{"
local sep = ""
for k in pairs(set) do
s = s..sep..k
sep = ", "
end
s = s.."}"
return s
end
function Set.print(set)
print(Set.tostring(set))
end
do
Set.mt.__add = Set.union
Set.mt.__mul = Set.intersection
s1 = Set.New{10,20,30,40}
s2 = Set.New{1,2,10,20}
--[[s = s1 + s2--]]
Set.print(s1 * s2)
end
先声明一个Set表用于存放我们所有的内容。
在Set表中声明一个mt的表用于当作其他表的metatable
Set.New(t),构造器,用于生成一个新的表,这个表的metatable是Set.mt
Set.union(a,b),用于对两个表做合集的操作
Set.intersection(a,b),用于对两个表做交集的操作
Set.tostring(set) 和 Set.print是用于打印表的。
然后将合集的操作定义成metatable中相加的操作,
将交集的操作定义成metatable中相乘的操作,如下:
将Set.mt.__add 指向 Set.union
将Set.mt.__mul 指向 Set.intersection
最后,用构造器,构造两个新的表,它们的metatable都是Set.mt,对它们进行相加操做的时候,lua会检查第一个参数的metatable中的__add域(metamethods),如果有,就用这个函数进行相加(合集)。
要特别声明一下,假设上述的表和其他类型的数值相加会怎么样?
lua中不关心(可以理解成,不考虑)表和其他类型相加,举个例子:
s + 8
lua会到s的metatable中寻找__add的域,然后它发现第二个操做数不是表,这个__add的函数就会报错。如果要获取具体的报错信息,可以在对_add的函数内部对两个参数加上一层检测,来提示自己是合集操做时出错。