Lua元表(Metatable)
在 Lua table 中我们可以访问对应的key来得到value值,但是却无法对两个 table 进行操作。
因此 Lua 提供了元表(Metatable),允许我们改变table的行为,每个行为关联了对应的元方法。
举个例子:
local t1 = {1}
local t2 = {2}
local t3 = t1 + t2
就会报错:
lua: xx.lua:3: attempt to perform arithmetic on a table value (local 't1')
原因:
当Lua试图对两个表进行相加时,先检查两者之一是否有元表,之后检查是否有一个叫"__add"的字段,若找到,则调用对应的值。"__add"等即时字段,其对应的值(往往是一个函数或是table)就是"元方法"。
下面我们就一步步来介绍如何实现这样的集合运算。
在table中,我可以重新定义的元方法有以下几个:
元方法 | 说明 |
---|---|
__add(a, b) | –加法 |
__sub(a, b) | –减法 |
__mul(a, b) | –乘法 |
__div(a, b) | –除法 |
__mod(a, b) | –取模 |
__pow(a, b) | –乘幂 |
__unm(a) | –相反数 |
__concat(a, b) | –连接 |
__len(a) | –长度 |
__eq(a, b) | –相等 |
__lt(a, b) | –小于 |
__le(a, b) | –小于等于 |
__index(a, b) | –索引查询 |
__newindex(a, b, c) | –索引更新(PS:不懂的话,后面会有讲) |
__call(a, …) | –执行方法调用 |
__tostring(a) | –字符串输出 |
__metatable | –保护元表 |
__gc | 在 table 被回收时会触发的回调,可以用来做一些 lua内存泄露 及 资源释放等操作这个方法在 lua5.1 版本是不支持的,5.2以上的版本才可以直接使用。 |
有两个很重要的函数来处理元表:
setmetatable(table,metatable): 对指定 table
设置元表(metatable),如果元表(metatable)中存在 __metatable 键值,setmetatable 会失败。
getmetatable(table): 返回对象的元表(metatable)
t = {}
print(getmetatable(t))
输出nil
t = {}
t1 = {}
setmetatable(t, t1)
print(getmetatable(t) == t1)
输出true
说明:setmetatable(t, t1) 是将t1作为t的元表;getmetatable(t)是获取t的元表——即t1
我们仍然用自定义一个集合来来说明如何自定义元方法
1.创建一个集合
Set = {}
local mt = {}
function Set.new(l)
local set = {}
setmetatable(set, mt)
for _, v in pairs(l) do
set[v] = true
end
return set
end
2.打印集合
local s1 = Set.new{2, 4}
print(s1)
这个print会报错,原因是它会先找s1的元方法__tostring,如果没有这元方法就报错,而这个元方法是返回一个字符串,print调用就可以打印出来。
我们在上面的代码上添加
function Set.tostring(set)
local tb = {}
for k, _ in pairs(set) do
tb[#tb + 1] = k
end
return "{" .. table.concat(tb, ", ") .. "}"
end
mt.__tostring = Set.tostring
这时候:
local s1 = Set.new{2, 4}
print(s1)
输出{2, 4}
3.输出集合的长度
我们需要先先实现__len元方法
--计算集合长度,即集合中元素的个数
function Set.len(set)
local length = 0
for k, _ in pairs(set) do
length = length + 1
end
return length
end
mt.__len = Set.len
local s1 = Set.new{2, 4}
print(s1)
print(#s1)
输出
{2, 4}
2
全部代码:
Set = {}
local mt = {}
function Set.new(l)
local set = {}
setmetatable(set, mt)
for _, v in pairs(l) do
set[v] = true
end
return set
end
function Set.tostring(set)
local tb = {}
for k, _ in pairs(set) do
tb[#tb + 1] = k
end
return "{" .. table.concat(tb, ", ") .. "}"
end
--计算集合长度,即集合中元素的个数
function Set.len(set)
local length = 0
for k, _ in pairs(set) do
length = length + 1
end
return length
end
mt.__tostring = Set.tostring
mt.__len = Set.len
local s1 = Set.new{2, 4}
print(s1)
print(#s1)
接下来我们就继续实现这个集合的+,-,<= 等运算
参考:
Lua中的元表与元方法
lua元表详解
Lua 元表(Metatable)
lua5.1中实现__gc
Lua5.3中文帮助文档(云风翻译)