-
Lua:元表(metatable)与元方法(meatmethod)
-
元表概念:
-
引言:Lua中的每个值都有一套预定义的操作集合,如数字相加等。但无法将两个table相加,此时可通过元表修改一个值的行为,使其在面对一个非预定义的操作时执行一个指定操作。
-
访问机制:一般的元方法都只针对Lua的核心,也就是一个虚拟机。它会检测一个操作中的值是否有元表,这些元表是否定义了关于次操作的元方法。例如两个table相加,先检查两者之一是否有元表,之后检查是否有一个叫“__add”的字段,若找到,则调用对应的值。“__add”等即时字段,其对应的值(往往是一个函数或是table)就是“元方法”。
-
-
元表实例
-
setmetatable(只能用于table)和getmetatable(用于任何对象)
-
语法:setmetatable (table, metatable),对指定table设置metatable 【如果元表(metatable)中存在__metatable键值,setmetatable会失败】
-
语法:tmeta = getmetatable (tab),返回对象的元表(metatable) 【如果元表(metatable)中存在__metatable键值,当返回__metatable的值】
-
代码:
1234567891011print(getmetatable(
"lua"
)) -->table: 002F19B8
print(getmetatable(10)) -->nil
--使用__metatable可以保护元表,禁止用户访问元表中的成员或者修改元表。
tA = {}
mt = {}
getmetatable(tA, mt)
mt.__metatable =
"lock"
setmetatable(tA, mt)
print(getmetatable(tA)) -->
lock
-
- 算术类元方法: 字段:__add __mul __ sub __div __unm __mod __pow (__concat)
- 代码:(两个table相加)
1234567891011121314151617181920
tA = {1, 3}
tB = {5, 7}
--tSum = tA + tB
mt = {}
mt.__add = function(t1, t2)
for
_, item
in
ipairs(t2)
do
table.insert(t1, item)
end
return
t1
end
setmetatable(tA, mt)
tSum = tA + tB
for
k, v
in
pairs(tSum)
do
print(v)
end
- 代码:(两个table相加)
- 关系类元方法: 字段:__eq __lt(<) __le(<=),其他Lua自动转换 a~=b --> not(a == b) a > b --> b < a a >= b --> b <= a 【注意NaN的情况】
- 代码:
123456789101112
--比较集合大小 <
mt = {}
function mt.__lt(tA, tB)
return
#tA < #tB
end
tA, tB = {3}, {1, 2}
setmetatable(tA, mt)
setmetatable(tB, mt)
print(tA < tB)
- 代码:
-
table访问的元方法: 字段: __index __newindex
-
__index:
查询:访问表中不存的字段
rawget(t, i) -
__newindex:
更新:向表中不存在索引赋值
rawswt(t, k, v)
-
-
-
贯穿《Programming in Lua》元表与元方法整张的实例
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146<span style=
"font-family: 黑体;"
>--[[
Set = {}
mt = {} --元表
function Set.
new
(l)
local
set
= {}
setmetatable(
set
, mt)
for
_, v
in
ipairs(l)
do
set
[v] =
true
end
return
set
end
--================================================
function Set.tostring(
set
)
local l = {}
for
e
in
pairs(
set
)
do
l[#l + 1] = e
end
return
"{"
.. table.concat(l,
","
) ..
"}"
end
function Set.print(s)
print(Set.tostring(s))
end
--1 加(__add), 并集===============================
function Set.union(a, b)
--[[
if
getmetatable(a) ~= mt or getmetatable(b) ~= mt then
error(
"attemp to 'add' a set with a non-set value"
, 2) --error第二个参数的含义P116
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
s1 = Set.
new
{10, 20, 30, 50}
s2 = Set.
new
{30, 1}
--print(getmetatable(s1))
--print(getmetatable(s2))
mt.__add = Set.union
s3 = s1 + s2
--Set.print(s3)
--[[元表混用
s = Set.
new
{1, 2, 3}
s = s + 8
Set.print(s + 8)
]]
--2 乘(__mul), 交集==============================
function Set.intersection(a, b)
local res = Set.
new
{}
for
k
in
pairs(a)
do
res[k] = b[k]
end
return
res
end
mt.__mul = Set.intersection
--Set.print(s2 * s1)
--3 关系类===================================NaN的概念====
mt.__le = function(a, b)
for
k
in
pairs(a)
do
if
not b[k] then
return
false
end
end
return
true
end
mt.__lt = function(a, b)
return
a <= b and not (b <= a)
end
mt.__eq = function(a, b) --竟然能这么用!?----
return
a <= b and b <= a
end
g1 = Set.
new
{2, 4, 3}
g2 = Set.
new
{4, 10, 2}
print(g1 <= g2)
print(g1 < g2)
print(g1 >= g2)
print(g1 > g2)
print(g1 == g1 * g2)
--]]
--============================================
--4 table访问的元方法=========================
--[[
--__index有关继承的典型示例
Window = {}
Window.prototype = {x = 0, y = 0, width = 100, height}
Window.mt = {}
function Window.
new
(o)
setmetatable(o, Window.mt)
return
o
end
Window.mt.__index = function (table, key)
return
Window.prototype[key]
end
w = Window.
new
{x = 10, y = 20}
print(w.width)
--__index修改table默认值
function setDefault (t, d)
local mt = {__index = function ()
return
d end}
setmetatable(t, mt)
end
tab = {x = 10, y = 20}
print(tab.x, tab.z)
setDefault(tab, 0)
print(tab.x, tab.z)
--]]
--13.4.5 只读的table
function readOnly(t)
local proxy = {}
local mt = {
__index = t,
__newindex = function(t, k, v)
error(
"attempt to update a read-only table"
, 2)
end
}
setmetatable(proxy, mt)
return
proxy
end
days = readOnly{
"Sunday"
,
"Monday"
,
"Tuesday"
,
"W"
,
"T"
,
"F"
,
"S"
}
print(days[1])
days[2] =
"Noday"
</span>
-