Lua学习笔记(4)

元表与元方法

元表

Lua中的每个值都有一个元表。table和userdata可以有各自独立的元表,而其他类型的值则共享类型所属的单一元表。Lua在创建新的table时不会创建元表。
使用setmetatable来设置或修改任何table的元表。

1
2
3
4
t = {}
t1 = {}
setmetatable(t, t1)
assert(getmetatable(t) == t1)

任何table都可以作为任何值的元表,而一组相关的table也可以共享一个通用的元表,此元表描述了它们共同的行为。

Lua代码中,只能设置table的元表。若要设置其他类型的值的元表,则必须通过C代码来完成。

下面示例用来说明如何使用元表。

1
2
3
4
5
6
7
8
set = {}
local mt = {}
function Set.new(l)
local set = {}
setmetatable(set, mt)
for _, v in ipairs(l) do set[v] = true end
return set
and
1
2
3
4
5
6
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
1
2
3
4
5
6
7
function Set.intersection(a, b)
local res = Set.new{}
for k in pairs(a) do
res[k] = b[k]
end
return res
end
1
2
3
4
5
6
7
function Set.tostring(set)
local l = {}
for e in pairs(set) do
l[#l + 1] = e
end
return "{" .. table.concat(l, ", ") .. "}"
end
1
2
3
4
5
6
mt.__le = function(a, b)
for k in pairs(a) do
if not b[k] then return false end
end
return true
end
1
2
3
mt.__lt = function(a, b)
return a <= b and not (b <= a)
end
1
2
3
mt.__eq = function(a, b)
return a <= b and b <= a
end
1
2
3
4
5
mt.__add = Set.union
s1 = Set.new{10, 20, 30, 50}
s2 = Set.new{30, 1}
s3 = s1 + s2
Set.print(s3) --> (1, 10, 20, 30, 50)
1
2
3
4
5
6
7
s1 = Set.new{2, 4}
s2 = Set.new{4, 10, 2}
print(s1 <= s2) --> true
print(s1 < s2) --> true
print(s1 >= s1) --> true
print(s1 > s1) --> false
print(s1 == s2 * s1) --> true

在元表中,每种算术操作符都有对应的字段名。

  • __add(+) –> 加法
  • __mul(*) –> 乘法
  • __sub(减法) –> 减法
  • __div(除法) –> 除法
  • __unm(相反数)
  • __mod(取模)
  • __pow(乘幂)
  • __concat(描述连接操作符的行为)
  • __le(<=) –> 子集
  • __lt(<) –> 真子集
  • __eq(==) –> 相等
  • __len –> 长度
  • __index –> 索引查询
  • __newindex –> 索引更新

###table访问的元方法

####__index元方法

当访问一个table中不存在的字段时,这些访问会促使解释器去寻找一个叫__index的元方法,如果没有这个元方法,那么访问结果为nil。否则,就由这个元方法来提供最终结果。

下面将介绍一个有关继承的典型示例。

1
2
3
4
5
6
7
8
9
Window = {} -- 创建一个名字空间
-- 使用默认值创建一个原型
Window.prototype = {x=0, y=0, width=100, height=100}
Window.mt = {} -- 创建元表
-- 声明构造函数
function Window.new(o)
setmetatable(o, Window.mt)
return o
end
1
2
3
Window.mt.__index = function(table, key)
return Window.prototype[key]
end
1
2
w = Window.new{x=10, y=20}
print(w.width) --> 100

下面通过__index元方法实现一个具有默认值的table

1
2
3
4
function setDefault (t, d)
local mt = {__index = function () return d end}
setmetatable(t, mt)
end
1
2
3
4
tab = {x=10, y=20}
print(tab.x, tab.z) --> 10 nil(因为没有设置元表,所以tab.z返回了nil)
setDefault(tab, 0)
print(tab.x, tab.z) --> 10 0