Lua-元表的使用

元表使用部分讲解

本文主要用于自己学习内容的总结,有错误欢迎指出,谢谢。


元表的作用

元表的目的是用来定义table 和 userdata 的表 (大概像一个使用说明)

--两种常用的方法
setmetatable(table,setmetatable) --对指定 table 设置元表(metatable),如果元表(metatable)中存在 
__metatable 键值,setmetatable 会失败。
getmetatable(table) --返回对象的元表(metatable)

举个列子:

local t1 ={1}
local t2 ={2}
local t3 = t1 + t2

运行上面的代码就会报错,因为我们执行了两个table 相加的操作,但是Lua并不知道如何将两个table相加。此时我们就需要重新定义table的运算规则,有点像c语言中重载struct的操作符


算术类的元方法

假设我们使用table作为集合,并且使用一些函数用来计算集合的交集和并集。为了保持命名空间的整齐,将这些函数写入到名为Set的table中。

Set = {}
function Set.new(l) --根据参数列表l的值创建一个新的集合
 local set ={}
 for _,v in ipairs(l) do
   set[v] = true 
 end
 return set
end

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

function Set.intersection(a,b)--去交集
 local res = Set.new{}
 for k in pairs(a) do res[k] = b[k] end
 return res
end
--打印集合函数,方便检测
function Set.tostring(set)
 local l = {}
 for e in pairs(set) do 
  l[#l+1] = e
 end
 return "{"..table.concat(l.",").."}"
end

function Set.print(s)
 print(Set.tostring(s))
end

现在我们使用 + 表示计算两个集合的交集,那么此时我就需要让这两个集合共用一个元表,并且要在元表中定义如何执行加法的操作。

local mt = {} --集合的元表

此时我们需要修改Set.new函数。该函数用于创建集合,在新版本中加入一行,将mt设置为当前创建table的元表:

function Set.new(l)
 local set = {}
 setmetatable(set,mt)
 for _,v in ipairs(l) do set[v] = true end
 return set
end
--此时我们使用Set.new所创建的集合都共用了一个元表(mt)
s1 = Set.new(10,20,30,50)
s2 = Set.new(40,60)
--此时我们需要把元方法加入到元表中,__add 这个字段描述将加法加入到这个元表中。
--+表示两个集合的并集
mt.__add = Set.union
s3 = s1 + s2
Set.print(s3) --->(10,20,30,40,50,60)
--类似用*表示两个集合相交,只需把 __mul 加入到元表中
mt.__mul = Set.intersection
s3 = (s1+s2)*s1
Set.print(s3) -->(10,20,30,50)

算术元方法

函数描述
__add运算符 +
__sub运算符 -
__mul运算符 *
__div运算符 /
__pow运算符 ^(幂函数)
__mod运算符 %
__unm运算符 -(取反)
__concat运算符 …(连接)

关系类的元方法

和算术类基本相同。但是有三种是关系运算符是不存在的(~=,>,>=)。Lua中会把 ~= 转换为 not (a==b),a>b 转化为b<a,将a>=b 转化为了b<=a。以上面集合为例,在集合操作中,通常<=表示集合间的包含关系:a<=b通常意味着a是b的一个子集。

关系元方法

函数描述
__eq运算符 ==
__lt运算符 <
__le运算符 <=

我们来具体实现一下代码:

mt.__le = function (a,b)
  for k in pairs(a) do 
    if not b[k] then return false end
  end
  return true
end

mt.__lt = funtion (a,b)
  return a<=b and not (b<=a)
end

mt.__eq = function (a,b)
  return a<=b and b<=a
end
    
s1 = Set.new{2,4}
s2 = Set.new{2,4,10}
print(s1 <= s2)     -->true
print(s1 < s2)      -->true
print(s1 >= s1)     -->true
print(s1 > s2)      -->false
print(s1 = s2 * s1) -->true
    

算术类和关系类不同

关系类的元方法不能缓和类型。如果尝试用一个字符串比较数字类型的话,Lua就会报错。


库定义的元方法

各种程序库在元表中定义他们自己的字段是很普遍的方法。到目前所有的元方法只针对于Lua的核心,也就是一个虚拟机。它会检测一个操作中的值是否有元表,这些元表中是否定义了关于此操作的元方法。从某一方面说,元表也是一种常规的table,任何人,任何函数都可以使用它。
函数tostring就是一个经典案例,tostring可以将各种类型的值表示为简单的文本格式:

print({})  --> table:

函数print总是调用tostring来格式化输出。当格式化任意值时,tostring会检查该值是否有一个__tostring的元方法,如果有这个元方法,tostring就用该值作为参数来调用这个元方法。接下来由这个元方法来完成工作,其返回结果也是tostring的结果。接下来我们来使用上面写到的代码:

mt.__tostring = Set.tostring
s1 = Set.new{10,2,5}
print(s1)   --> {2,5,10}

call函数可以让table当作函数来访问:

function Set.call(...)
  for _,v in ipairs {...} do
    print(v)
  end
end
mt.__call = Set.call
s1 = Set.new{}
s1(1,2,3,4)  -->1,2,3,4
函数描述
__tostring转化为字符串类型
__call将table当作函数

table 访问的元方法

Lua的表本质其实是个类似HashMap的东西,其元素是很多的Key-Value对。如果尝试访问了一个表中并不存在的元素时,就会返回一个nil值,这样说不全对,实际上当访问table中不存在的字段时,会使解释器去查找一个__index的元方法。如果这个元方法不存在,才会返回这个nil值。而且__index这个元方法和前面不同,__index即可以是个函数,也可以是一个table。
__index作为函数方法实现:

function Set.index(t,key)  --t表示自己,key表索引
  return "not_find_"..key
end
mt.__index = Set.index
s1 = {1,2,3}
print(s1.key) -->nil
s2 = Set.new{1,2,3}
print(s2.key)  --> not_find_key

__index作为table实现:

mt.__index = {key = "not_find_key"}
s1 = Set.new{1,2,3}
print(s1.key) --> not_find_key
print(s1.key2)  --> nil

__index作为table 和函数时效果相似,但也不一样,如上面的例子。因为我们的key2 没有定义,所以Lua再查找时,就返回了nil。


除了__index的元方法,table中还存在一种__newindex的元方法。两者相似但又不同,前者用于查询,而后者则用于table的更新。如果__newindex元方法是一个table,解释器就会在这个table中赋值,而不是原来的table。
作为函数时:

mt.__newindex = function(t,index,value)
  print("index is"..index)
  print("value is"..value)
end
s1 = {key = "it is key"}
setmetatable = (s1,mt)
print(s1) --> it is key --表中存在索引key,直接输出
s1.newKey = 10
print(s1.newKey) -->index is newKey  --表中并不存在newKey索引,所以调用元方法。
-->value is 10
-->nil

作为table时:

local newTable = {}
mt.__newindex = newTable
t={}
setmetatable(t,mt)
print(t.key,newTable.key) --> nil nil
t.key = "it is key"
print(t.key,newTable.key) --> nil it is key
函数描述
__index访问一个索引
__newindex给索引赋值

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值