lua元表

本文简译自一篇老外的博客,写得不错可惜我翻译的太烂,简译如下。

(key–value常见翻译为“键值对”,我翻译为索引、值)

在这篇教程里我会介绍Lua中一个重要的概念: metatable(元表),掌握元表可以让你更有效的

使用Lua。 每一个tabel都可以附加元表, 元表是带有索引集合的表,它可以改变被附加表的行为。

看下例:

t = {} – 普通表
mt = {} – 元表,现在暂时什么也没有
setmetatable(t, mt) – 把mt设为t的元表
getmetatable(t) – 这回返回mt

如你所见 getmetatable 和setmetatable 是主要的函数。 当然我们可以把上面的三行代码合为:

t = setmetatable({}, {})

setmetatable 返回第一个参数, 因此我们可以使用这个简短的表达式。现在,我们在元表里放些什

么呢? 元表可以包含任何东西,但是元表通常以”__”(两个下划线)开头的索引(当然string类型)

来调用,例如__index和__newindex。 和索引对应的值可以是表或者函数,例如:

t = setmetatable({}, {
__index = function(t, key)
if key == “foo” then
return 0
else
return table[key]
end
end
})
我们给__index索引分配了一个函数, 让我们来看看这个索引是干啥的。

__index
元表里最常用的索引可能是__index,它可以包含表或函数。

当你通过索引来访问表, 不管它是什么(例如t[4], t.foo, 和t[“foo”]), 以及并没有分配索引的值时,

Lua 会先在查找已有的索引,接着查找表的metatable里(如果它有)查找__index 索引。 如果

__index 包含了表, Lua会在__index包含的表里查找索引。 这听起来很迷糊,让我们看一个例子。

other = { foo = 3 }
t = setmetatable({}, { __index = other })
t.foo – 3 ,现在__index包含的表{foo=3}查找
t.bar – nil ,没找到

如果__index 包含一个函数,当被它调用时,会把被访问的表和索引作为参数传入。从上面的例子来看,

我们可以使用带有条件语句的索引,以及任意的Lua语句。因此在这种情况下,如果索引和字符串”foo”

相等,我们可以返回0,否则,我们可以查询表中被使用的索引;当”foo”被使用时,让t作为table的

别名并返回0。(这句不是太懂,原文为:Therefore,in that example, if the key was equal to

the string “foo” we would return 0, otherwise we look up the table table with the key that

was used; this makes t an alias of table that returns 0 when the key “foo” is used.)

你可能会疑问,怎么把表作为是第一个传给__index 函数的参数。当你在多个表里使用相同的元表时,

这会很方便,并支持代码复用和节省电脑资源。我们会在最下面的Vector 类里看到解释。

–注:下面是我的一个例子

other = function(t,k) if k==”foo” then return 0 end end
t = setmetatable({}, { __index = other })
print(t.foo)

__newindex
下一个是__newindex, 它和__index类似。和 __index一样,它可以包含函数和表。当你给表中不存在

的值赋值时,Lua会在metatable里查找__newindex,调用顺序和 __index一样。如果__newindex是表,

索引和值会设置到指定的表:

other = {}
t = setmetatable({}, { __newindex = other })
t.foo = 3 –t里没有foo,查看__newindex,并把foo=3传给了other,并没有给t里的foo赋值
other.foo – 3 故为3
t.foo – nil 故为 nil
和期望的一样,__newindex 是函数时,当被调用时会传递表、索引、值三个参数。

t = setmetatable({}, {
__newindex = function(t, key, value)
if type(value) == “number” then
rawset(t, key, value * value)
else
rawset(t, key, value)
end
end
})

t.foo = “foo”
t.bar = 4
t.la = 10
t.foo – “foo”
t.bar – 16
t.la – 100

当在t里创建新的索引时,如果值是number,这个值会平方,否则什么也不做。下面介绍rawget 和rawset。

rawget 和 rawset
有时需要get 和set表的索引,不想使用metatable.你可能回猜想, rawget 允许你得到索引无需__index,

rawset允许你设置索引的值无需__newindex (不,相对传统元表的方式,这些不会提高速度)。为了避免陷

在无限循环里,你才需要使用它们。 在上面的例子里, t[key] = value * value将再次调用__newindex

函数,这让你的代码陷入死循环。使用rawset(t, key, value * value) 可以避免。

你可能看到,使用这些函数, 我们必须传递参数目标table, key, 当你使用rawset时还有value。

操作符
许多元表的索引是操作符 (如, +, -, 等),允许你使用表完成一些操作符运算。例如,我们想要一个表支持

乘法操作符(*), 我们可以这样做:

t = setmetatable({ 1, 2, 3 }, {
__mul = function(t, other) ,
new = {}

for i = 1, other do 
  for _, v in ipairs(t) do table.insert(new, v) end 
end 

return new 

end
})

t = t * 2 – { 1, 2, 3, 1, 2, 3 }

这允许我们创建一个使用乘法操作符重复某些次数的新表。你也看的出来, __mul和乘法相当的索引是,

与__index、 __newindex 不同,操作符索引只能是函数。 它们接受的第一个参数总是目标表, 接着

是右值 (除了一元操作符“-”,即索引__unm)。下面是操作符列表:

__add: 加法(+)
__sub: 减法(-)
__mul: 乘法(*)
__div: 除法(/)
__mod: 取模(%)
__unm: 取反(-), 一元操作符
__concat: 连接(..)
__eq: 等于(==)
__lt: 小于(<)
__le:小于等于(<=)
(只有==, <, <= ,因为你能通过上面的实现所有操作,事实上== 和<就足够了)

__call
接下来是__call 索引, 它允许你把表当函数调用,代码示例:

t = setmetatable({}, {
__call = function(t, a, b, c, whatever)
return (a + b + c) * whatever
end
})

t(1, 2, 3, 4) –- 24 ,表t在调用时先查找__call,调用里面的函数,t便相当于函数了
和通常一样在call里的函数,被传递了一个目标表,还有一些参数。__call 非常有用,经常用来在表和它

里面的函数之间转发调用(原文it’s used for is forwarding a call on a table to a function inside

that table.)。 kikito的 tween.lua 库就是个例子tween.start可以被自身调用(tween). 另一个例子是

MiddleClass, 类里的new函数可以被类自身调用。

__tostring
最后一个是 __tostring。如果实现它,那么tostring 可以把表转化为string, 非常方便类似print的函数

使用。 一般情况下,当你把表转为string时, 你需要”table: 0x

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值