Lua从入门到放弃--元表

前两天生病了,一直没更新博客,以后我争取每天更新一篇,如果周六日没特殊情况的话也会照常更新。废话不多说,这篇就来讲讲lua中的元表。


元表(metatable)是lua中一个很重要的概念,掌握元表可以让你有效的使用lua。每一个table都可以附加元表,元表是带有索引集合的表,它改变被附加表的行为。这句话可能听上去有点晕,那下面就让我们举例说明一下这个概念。例:

t = {}    -- 普通表

mt = {}    --  元表,现在这里面什么都没放

setmetatable(t, mt)    --  使用setmetatable函数就把mt设置为t的元表了

getmetatable(t)    --  使用getmetatable函数可以获取t的元表,也就是mt

当然,上面的三行代码也可以合并成一行:

t = setmetatable({}, {})

这里setmetatable返回第一个参数, 也就是{}, 因此我们可以使用这个简短的表达式。接下来我们要在元表中放入一些东西,元表可以包含任何东西,但是元表通常以"__"(两个下划线)开头的索引来调用,例如__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    --  先在t表中查找,没找到;然后在__index包含的表{ foo = 3 }查找,返回3这个值

t.bar    --  两张表均没找到bar这个字段,So,返回nil

如果__index包含一个函数,当它被调用时,会把访问的表和索引作为参数传入。从上面的例子来看,我们可以使用带有条件语句的索引,以及任意的lua语句。因此在这种情况下,如果索引和字符串"foo"相等,我们可以返回0,否则,我们可以查询表中被使用的索引;当"foo"被使用时,让t作为table的别名并返回0。如:

other = function(t, k) if k == "foo" then return 0 end end

t = setmetatable({}, {__index = other})

print(t.foo)    -- t的元表为一个函数,函数第一个参数为表t, 第二个参数为"foo" ,此时结果为0


__newindex

上面我们讲了__index,接下来让我们来看看__newindex, 它和__index类似,也可以包含函数和表。当你给表中不存在的字段赋值时,lua会在metatable里查找__newindex, 调用顺序和__index一样。如果__newindex是表,索引和值会设置到指定的表,来看一下下面的例子:

other = {}

t = setmetatable({}, {__newindex = other})

t.foo = 3    --  t表里没有foo这个字段,查看__newindex,如果__newindex中有foo这个字段,则会将这个字段重新赋值为3,如果没有则会生成出一个foo字段并将其赋值为3。这里的赋值并不会影响t表。

print(t.foo, other.foo)    --  这里打印t.foo和other.foo, 结果为nil, 3。印证了我们上面的说法。

当__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

print(t.foo, t.bar, t.la)    --  结果为"foo", 16, 100

当在t里面创建新的索引时, 如果值是number, 这个值会平方, 否则什么都不做。


rawget和rawset

上面的例子,我们有用到rawset。有时我们需要get和set表的索引,不想使用metatable, 你可能会猜想,rawget允许你得到索引无需__index, rawset允许你设置索引值无需__newindex。为了避免陷入在无线循环里,你才需要使用它们。为什么这么说,可能有人会想到,给表赋值直接使用之前说的table构造不就可以了么,例如t[key] = value * value, 如果这样写将再次调用__newindex函数,这让你的代码陷入死循环,处理方法就是使用rawset(t, key, value * value) 。


元表的基本概念讲的差不多了,还有一些关于操作符等内容将放到下一篇讲解。

本文参考lua手册  http://www.lua.org/manual/5.3/manual.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值