Lua学习笔记-metatable元表

   本以为看完C API lua就学的差不多了,没想到越陷越深啊。

   首先说元表的概念,元表是一个特殊的表,作用是定义一个表的操作(metamethod元方法)。类似于C++中类的运算符重载。

   使用元表需要注意的事项:

1.元表可以定义的metamethod有下面这些

 1 __add(a, b) --加法
 2 __sub(a, b) --减法
 3 __mul(a, b) --乘法
 4 __div(a, b) --除法
 5 __mod(a, b) --取模
 6 __pow(a, b) --乘幂
 7 __unm(a) --相反数
 8 __concat(a, b) --连接
 9 __len(a) --长度
10 __eq(a, b) --相等
11 __lt(a, b) --小于
12 __le(a, b) --小于等于
13 __index(a, b) --索引查询
14 __newindex(a, b, c) --索引更新
15 __call(a, ...) --执行方法调用
16 __tostring(a) --字符串输出
17 __metatable --保护元表
View Code

2.上面的操作其实是table中一个键对应一个函数,键字符串前面是双下划线(简直坑啊,第一次写半天没发现错误)

3.如果两个表都有定义元表,那么按照下面原则调用:

  • 对于二元操作符,如果第一个操作数有元表,并且元表中有所需要的字段定义,比如我们这里的__add元方法定义,那么Lua就以这个字段为元方法,而与第二个值无关;
  • 对于二元操作符,如果第一个操作数有元表,但是元表中没有所需要的字段定义,比如我们这里的__add元方法定义,那么Lua就去查找第二个操作数的元表;
  • 如果两个操作数都没有元表,或者都没有对应的元方法定义,Lua就引发一个错误。

 

元表的设置与获取

1 setmetatable(table,metatable): 对指定table设置元表(metatable),如果元表(metatable)中存在__metatable键值,setmetatable会失败 。
2 getmetatable(table): 返回对象的元表(metatable)。

 

下面是例子,这个例子是简单的定义一个“+”运算符,将两个table中对应键的值相加再返回一个新表。至于表的大小是否相等,对应位置类型是否一致,没有做错误处理,这些不是重点。

 1 function add(a,b)            --定义__add需要用到的函数
 2  local sum={}
 3  for i,v in ipairs(a) do
 4   sum[i]=a[i]+b[i]
 5  end
 6 return sum
 7 end
 8 
 9 a={1,2,3}         
10 b={9,8,7}
11 t={__add=add}          --声明一个表t作为元表,其中__add键对应的值为add函数
12 setmetatable(a,t)      --设置a的元表为t
13 sum=a+b                --计算a+b,返回新表sum,并且打印
14 for i in ipairs(sum) do
15 print(sum[i])
16 end
17 
18 运行结果:
19 10
20 10
21 10

 

逻辑运算符和单目运算符的用法类似。

 

__index元方法

当我们访问一个table中不存在的键时,会返回nil,这个元方法定义了如果访问不存在的键时的操作。

  1. 当访问一个table的字段时,如果table有这个字段,则直接返回对应的值;
  2. 当table没有这个字段,则会促使解释器去查找一个叫__index的元方法,接下来就就会调用对应的元方法,返回元方法返回的值;
  3. 如果没有这个元方法,那么就返回nil结果。

接着上面的例子

1 function index(t,key)        --定义__index需要用到的函数
2 return "key "..key.." is nil"
3 end
4 
5 t.__index=index
6 print(a[4])
7 
8 运行结果:
9 key 4 is nil

 

当然,这里__index也可以对应一个table,这种情况会在这个table中查找相应的键并且重复上面的1,2,3,有点类似递归调用,下面的例子稍微有点复杂,所以贴出完整代码

 1 function index(t,key)        --定义__index需要用到的函数
 2 return "key "..key.." is nil"
 3 end
 4 
 5 a={}
 6 b={}
 7 c={}
 8 d={}
 9 b.__index=c
10 d.__index=index
11 setmetatable(a,b)     
12 setmetatable(c,d)
13 print(a[4])
14 
15 输出结果:
16 key 4 is nil

当打印a[4]时,由于key4不存在,所以会去a的元表b中找__index元方法,此时b的__index对应的是一个表c,那么就变成在c中查找key4,发现c中也没有key4,所以在c的元表d中找__index元方法,发现__index对应了index函数,所以此时调用index函数,输出结果。

如果c中存在key4,那么就会返回c的key4,如下

 1 a={}
 2 b={}
 3 c={[4]="4"}
 4 d={}
 5 b.__index=c
 6 d.__index=index
 7 setmetatable(a,b)
 8 setmetatable(c,d)
 9 print(a[4])
10 
11 输出结果:
12 4

如果看不懂也没关系,没有人闲着没事干搞这么多层元表,上面的例子仅仅为了深入理解。

 

__tostring元方法

这个方法定义了当我们使用print函数打印值时,输出的内容,比如我们print一个table,那么会显示它在内存中的地址,如果我们希望这个时候能打印出该table的键值对,就需要定义__tostring元方法

接着上面例子

 1 function printTable(t)      --定义打印table函数
 2  local s=''
 3  for i,v in pairs(t) do
 4  s=s..'('..i..','..v..')'
 5  end
 6  return s
 7 end
 8 
 9 t.__tostring=printTable
10 print(a)
11 
12 运行结果:
13 (1,1)(2,2)(3,3)

 

posted @ 2017-08-29 12:35  Initial_Dream  阅读(437)  评论(0编辑  收藏  举报