最新看了文档,学习了一下第13章Metatables and Metamethods。对于metatable 和 metamethods这两个单词,我看的中文文档里面没有翻译,google翻译是元表,元操作。本文中会涉及到个人对这个章节的一些理解,也是刚学如果有不正确的地方,希望各位大神指出。
一.metatable and metamethods
1.metatable. 元表,基表。在C++ 中,我们子类继承基类之后,我们可以使用基类的部分方法。相似的,在Lua中,我们把t1设置为t2的metatable,当我们对t2进行各种操作的时候,所使用的方法不是t2所具备的,比如执行操作O1,这是会去查找t2的metatable也就是t1是否具备O1这个操作方法,如果有则调用。
举一个简单的例子:
local t1 = {1, 2, 3}
local t2 = {4, 5, 6}
print(t1 + t2) -- attempt to perform arithmetic on local 't1'(a table value)
当我们尝试把两个表相加的时候,就出错了。这时候,我们为这两个表定义一个metatable mt:
mt = {}
setmetatable(t1,mt)
setmetatable(t2,mt)
并且为mt加一个__add域:
mt.__add = function (a,b)
local result = {}
if #a == #b then
for i=1,#a do
result[i] = a[i] + b[i]
end
else
print("wrong parameter")
end
return result
end
这时候我们执行: print(t1 + t2)就不会报错了。我们写个函数把相加的结果打印出来。下面是完整的代码及运行结果:
local t1 = {1, 2, 3}
local t2 = {4, 5, 6}
--print(t1 + t2) -- attempt to perform arithmetic on local 't1'(a table value)
mt = {}
setmetatable(t1,mt)
setmetatable(t2,mt)
mt.__add = function (a,b)
local result = {}
if #a == #b then
for i=1,#a do
result[i] = a[i] + b[i]
end
else
print("wrong parameter")
end
return result
end
function output (t)
for i=1,#t do
print(t[i])
end
end
output(t1 + t2)
2.metamethods. 在上面个这个里例子中,metatable mt的__add域就是一个metamethod。
二.算数运算的metamethods
我们知道算数运算符有加,减,乘,除,负,幂,metatable有着对应的域名:
__add, __sub, __mul, __div, __unm, __pow。还可以定义其他的行为,比如__concat定义连接行为。文档上面举了一个很好的例子关于集合的,__add函数作为求解两个集合的并集,__mul定义为两个集合的交集,在次上面,我加了一个__sub用来求解补集。下面是完整的代码和运行结果:
Set = {}
function Set.new (t)
local set = {}
for _,l in ipairs(t) do
set[l] = true
end
return set
end
function Set.union (a,b)
if getmetatable(a) ~= Set.mt or getmetatable(b) ~= Set.mt then
error("attempt to 'add' a set with a non-set value",2)
end
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.compliment (a,b)
local res = Set.new{}
for k in pairs(a) do
if not(b[k]) then
res[k] = true
end
end
return res
end
function Set.tostring (set)
local s = "{"
local sep = ""
for e in pairs(set) do
s = s..sep..e
sep = ", "
end
return s.."}"
end
function Set.print (s)
print(Set.tostring(s))
end
Set.mt = {} -- metatable for sets
function Set.new(t)
local set = {}
setmetatable(set,Set.mt)
for _,l in ipairs(t) do
set[l] = true
end
return set
end
s1 = Set.new{10,20,30,50}
s2 = Set.new{30,1}
print(getmetatable(s1))
print(getmetatable(s2))
Set.mt.__add = Set.union
Set.mt.__mul = Set.intersection
Set.mt.__sub = Set.compliment
s3 = s1 + s2
s4 = Set.new{1,5,10,20,30,50,45,78}
Set.print(s3) -- 输出s1和s2的并集
Set.print(s3*s1) -- 输出s1和s3的交集
Set.print(s4-s1) -- 输出s1关于s4的补集
三.关系运算的Metamethods
metatables提供的关系运算的metamethods有三个,__eq(等于),__lt(小于),和__le(小于等于),这三个可以让我们定义。对于另外的三个没有metamethod。Lua中是这么处理的:a~=b <=> not(a==b); a>b <=> b < a; a>=b <=>b<=a。这个是比较好理解的,文档提供了一个巧妙地例子,它首先定义__le,然后再通过__le去定义__lt和__eq,非常的简洁。
Set.mt.__le = function (a,b)
for k in pairs(a) do
if not b[k] then
return false
end
end
return true
end
Set.mt.__lt = function (a,b)
return a<=b and not(b<=a)
end
Set.mt.__eq = function (a,b)
return a <=b and b <=a
end
s1 = Set.new{2,4}
s2 = Set.new{4,10,2}
print(s1 <= s2)
print(s1 < s2)
print(s1 >= s1)
print(s1 > s1)
print(s1 == s1*s2)