lua脚本语言元表的理解

因后面相关项目会用到lua脚本,所以从0开始学习这种脚本语言。和shell及python类似,lua脚本语言也是解释性的,其变量没有类型,只有值有类型,类型有八种nil,number,boolean, string, function, thread, userdata以及table,这些基本类型中有table的概念,table是表有点类似于c语言的数组,也可以认为是一种hash结构,所以table的使用更加灵活。

table的定义形式为“{}”,如fruits={},这个是空表,fruits={“apple”, key=”mango”, “grape”},这个是带元素的表,其中元素apple,grape的索引是默认的1和2,我们可以使用print(fruits[1], fruits[2])打印输出,mango是通过print(fruits[“key2”])或者print(fruits.key2)输出,table默认从1开始,c和c++的数组都是从0开始的。

了解了table,我们再说说元表metatable,元表是带有索引集合的表,可以看做是特殊的表,他可以附加到table上,改变被附加table的行为。比如有两个表,idatas={1, 2, 3}和fdatas={1.0, 2.0 ,3.0},如果我们要实现两个表的求和,用其他语言需要挨个元素进行求和,但是如果使用元表则可以比较简单的实现。

现在我们先介绍一下元表附加到表的实现和查询方式

t={};//普通表table,暂定位空
mt={};//表metatable,暂停为空
setmetatable(t, mt); // 将mt设置为t的元表
getmetatable(t);    // 返回t的附加元表

函数setmetatable返回的是第一个参数,所以上面的设置可以简化为t=setmetatable({},{}),前一个{}是表t,后一个{}是元表mt。默认创建的table是不包含元表的,比如执行 print(getmetatable(fruits))将返回nil。

那么我们的元表中可以放置任何东西,系统提供的所以一般都是以“__”开头的,这些索引我们可以看做是c或c++中的函数,定义好metable中的“函数”行为后,就可以调用了。举例说明”__index”和”__newindex”,这两个是相对常用的元表索引

__index
__index是元表中最常用的索引,它可以包含表或函数;当你通过键来访问 table 的时候,如果这个键没有值,那么Lua就会寻找该table的metatable(假定有metatable)中的__index 键。如果__index包含一个表格,Lua会在表格中查找相应的键,下面是用lua(command line)交互模式演示:
交互截图
如图所示ta为普通表table,含有一个元素ta[“age”]或ta.age,tb是通过setmetatable返回第一个参数的”{}”,生成的table,它本身没有元素,但她有个附加的元表,附加元表__index指代的是ta。所以打印tb.age时,会去查找元表_index对应找到ta.age的值,而tb.name,则是没有。

__index也可以包含函数
如果__index包含一个函数的话,Lua会调用该函数,table和键会作为参数传递给函数。__index 元方法查看表中元素是否存在,如果不存在,返回结果为 nil;如果存在则由 __index 返回结果

ta = setmetatable({key0 = "abc"}, {
  __index = function(tx, key) --tx 和key是传入参数,具体是什么需调用时才确定
    if key == "key1" then
      return "metatable_value"
    else
      return nil
    end
  end
})

print(ta.key0,ta.key1)

执行结果为

abc   metatable_value

为了达到这样的目的,我们也可以将函数简化为

ta=setmetatable({key0="abc"}, {__index={key1="metatable_value"}}
print(ta.key0, ta.key1)

ta获取的是setmetatable返回的第一个参数即表{key0=”abc”},ta.key0是表ta本身的元素,所以是直接打印输出,ta.key1由于ta本身没有key1这个元素,所以lua会去查找对应的元表,元表中__index包含的是个函数,lua会将ta和对应的索引key1作为第一和第二参数带入到function中,执行结果是返回打印输出metatable_value,所以在定义__index对应的function时,函数至少要包含2个参数。

总结一下
Lua查找一个表元素时的规则,其实就是如下3个步骤:
1. 在表中查找,如果找到,返回该元素,找不到则继续
2. 判断该表是否有元表,如果没有元表,返回nil,有元表则继续。
3. 判断元表有没有__index方法,如果__index方法为nil,则返回nil;如果__index方法是一个表,则重复1、2、3;如果__index方法是一个函数,则返回该函数的返回值。

lua元表还有一种索引是__newindex
__newindex
__newindex用来对表进行更新操作,给表中不存在的索引赋值是,解释器就会查找__newindex 元方法:如果存在则调用这个函数,但不进行赋值操作。
以下实例演示了 __newindex 元方法的应用

mymetatable = {}  --1
mytable = setmetatable({key1 = "value1"}, { __newindex = mymetatable }) --2

print(mytable.key1)  --3

mytable.newkey = "新值2"  --4
print(mytable.newkey,mymetatable.newkey)  --5

mytable.key1 = "新值1"  --6
print(mytable.key1,mymetatable.key1)  --7

上面这段代码是摘自于,下面有部分代码也是源自此网站,借用一下。
http://www.runoob.com/lua/lua-metatables.html

运行结果

value1
nil    新值2
新值1    nil

下面来说明一下
代码2,mytable是setmetatable返回的第一个参数,所以mytable={key1=value}, 所以代码3,打印输出value1。mytable设置了元表,元表中__newindex的索引值是mymetatable,mymetatable是一个空表(代码1)。
再看代码5, 执行结果是mytable.newkey是nil不存在,mymetatable.newkey是“新值2”,我们可以敲一段代码测试,如果是普通表,只要赋新值,会立马生效

ta={key="value"}
ta.newkey="newvalue"
print(ta.key, ta.newkey) --执行结果为 value   newvalue

但由于mytable附带有元表,在执行操作是newkey本身是不存在的,所以lua去找元表对应的元方法,而不进行赋值,所以mytable.newkey实际是没有赋值,打印输出还是nil,再说一下mymetatable.newkey,我的判断是在执行mytable.newkey=”新值2”操作时,影响到了元表中__newindex指代的表mymetatable,使之由原来的空表变成{newkey=”新值2”}。具体我们可以再看一个程序

mymetatable = {}  --1
mytable = setmetatable({key1 = "value1"}, { __newindex = mymetatable }) --2

print(mytable.key1, mymetatable.key1, mymetatable.newkey) --3

mymetatable.newkey = "新值3"   --4

print(mytable.newkey,mymetatable.newkey)  --5

mytable.newkey = "新值2"   --6
print(mytable.newkey,mymetatable.newkey)  --7

mytable.key1 = "新值1"    --8
print(mytable.key1,mymetatable.key1) --9

执行结果为:

value1  nil     nil
nil     新值3
nil     新值2
新值1 nil

代码段4,给mymetatable新增元素newkey为”新值3”,执行代码5打印输出,证明元素添加成功,执行代码6后,此时mymetatable的newkey值已经变化,通过代码7打印可以看到值已经变为”新值2”。
代码8和9显示mytable中key1是存在的,所以更新后值是直接到位的,不去调用元方法__newindex。

如果想要实现已存在索引值的更新,请使用rawset函数

mytable = setmetatable({key1 = "value1"}, {
  __newindex = function(mytable, key, value)
        rawset(mytable, key, "\""..value.."\"")

  end
})

mytable.key1 = "new value"
mytable.key2 = 4

print(mytable.key1,mytable.key2)

执行结果为

new value    "4"

除_index 和__newindex外,还有其他的索引操作
模式 描述
__add 对应的运算符 ‘+’.
__sub 对应的运算符 ‘-‘.
__mul 对应的运算符 ‘*’.
__div 对应的运算符 ‘/’.
__mod 对应的运算符 ‘%’.
__unm 对应的运算符 ‘-‘.
__concat 对应的运算符 ‘..’.
__eq 对应的运算符 ‘==’.
__lt 对应的运算符 ‘<’.
__le 对应的运算符 ‘<=’.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值