• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
孙龙 程序员
少时总觉为人易,华年方知立业难
博客园    首页    新随笔    联系   管理    订阅  订阅
lua元表

举个例子,在 Lua table 中我们可以访问对应的key来得到value值,但是却无法对两个 table 进行操作。

那如何计算两个table的相加操作a+b?

local t1 = {1,2,3}
local t2 = {4,5,6} 

local t3 = t1 + t2 ----> {1,2,3,4,5,6}

类似java的一些操作重载

这种类似的需求,lua 提供了元表(Metatable)和元方法,允许我们改变table的行为,每个行为关联了对应的元方法。

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

2)getmetatable(table): 返回对象的元表(metatable)。

mytable = {} -- 普通表
mymetatable = {} -- 元表
setmetatable(mytable,mymetatable) -- 把 mymetatable 设为 mytable 的元表

等价于:
mytable = setmetatable({},{})

返回对象元表:
getmetatable(mytable) -- 返回mymetatable


元方法的命名都是以 __ 两个下划线开头。

一)__index 元方法
对表读取索引一个元方法

这是 metatable 最常用的键。

当你通过键来访问 table 的时候,如果这个键没有值,那么Lua就会寻找该table的metatable(假定有metatable)
中的__index元方法。如果__index是一个表,Lua会在表格中查找相应的键。

local t = {} -- 普通表t为空 
local other = {foo = 2} -- 元表 中有foo值
setmetatable(t,{__index = other}) -- 把 other 设为 t 的元表__index
print(t.foo); ---输出 2
print(t.bar); ---输出nil

 

t.foo为什么会输出2,就是因为我们重写了__index索引的重载,lua在执行中如果t中没有foo,
就会在他的元表中__index中去找,__index等于other,就输出2。

----------------

如果我们把t设置一下foo的值为3,在看看结果

local t = {foo = 3 } -- 普通表t为空 
local other = {foo = 2} -- 元表 中有foo值
setmetatable(t,{__index = other}) -- 把 other 设为 t 的元表__index
print(t.foo); ---输出 3
print(t.bar); ---输出nil

 

---------------------------------------------

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

local t = {key1 = "value1" } 
local function metatable(mytable,key)   
if key == "key2" then     return "metatablevalue"   else     return nil   end end setmetatable(t,{__index = metatable}) print(t.key1); print(t.key2); print(t.key3);

 

分析:

print(t.key1); ---这个输出value1 ,是因为t表中有此key
print(t.key2); ---这个输出metatablevalue,是因为t表中没有此key,就会调用t的元表中的元方法__index,
---这是__index是个函数,就会执行这个函数,传t表和key值这两个参数到此函数,
---函数体中判断有此key2 就输出metatablevalue;

print(t.key3); ---这个输出nil,是因为t表没有,元方法__index函数中 对key3返回nil值

--------------------------------------

总结:lua对表索引取值的步骤

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

 

二)__newindex 元方法

__newindex 元方法用来对表更新,__index则用来对表访问 。

当你给表进行索引进行赋值,但此索引不存在;lua会查找是否有元表,有元表就会查找__newindex 元方法是否存在:
如果存在则调用__newindex的值进行执行操作,但不会对原表进行赋值操作。

以下实例演示了 __newindex 元方法的应用:

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

print("mytable.key1=",mytable.key1)   #output : value1

mytable.newkey
= "新值2" print("mytable.newkey=",mytable.newkey) #nil 不会对原表进行赋值操作 print("mymetatable.newkey=",mymetatable.newkey) #新值2 mytable.key1 = "新值1" print("mytable.key1=",mytable.key1) #新值1 print("mymetatable.key1=",mymetatable.key1) #nil

 

以上实例中表设置了元方法 __newindex
在对新索引键(newkey)赋值时(mytable.newkey = "新值2"),会调用元方法,而对mytable原表不进行赋值。
而对mytable已存在的索引键(key1),则会进行赋值,而不调用元方法 __newindex。

----------------------------

如果我们要对原来的table进行赋值,那我们就可以用rawset;;__newindex函数会传三个参数,

mytable = setmetatable({key1 = "value1"}, { 
  __newindex = function(t,k,v) ---第一个参数为table,第二个参数为key,第三个参数为value
    rawset(t,k,v);
  end
})
mytable.key1 = "new value"
mytable.key2 = 4
print("mytable.key1=",mytable.key1)
print("mytable.key2=",mytable.key2)

 

key2原本是不再mytable表中的,通过元方法__newindex中函数使用了rawset,就可以对原table进行赋值。

 

 

三)为表添加操作符“+”

我们这里定义“+”这元方法,把它定义为两个table相连
如
t1={1,2,3}
t2={4,5,6}
t1 + t2 相加的结果,我们想得到的是 {1,2,3,4,5,6}
那我们如何写元表?
“+”对应的元方法为__add

local function add(mytable,newtable)
  local num = table.maxn(newtable)
  for i = 1, num do
    table.insert(mytable,newtable[i])
  end
  return mytable
end

local t1 = {1,2,3}
local t2 = {4,5,6}

setmetatable(t1,{__add = add})

t1 = t1 + t2

for k,v in ipairs(t1) do
  print("key=",k," value=",v)
end

 

这样我们就实现了两个table相加


以下是我们的操作符对应关系
模式 描述

  • __add 对应的运算符 '+'.
  • __sub 对应的运算符 '-'.
  • __mul 对应的运算符 '*'.
  • __div 对应的运算符 '/'.
  • __mod 对应的运算符 '%'.
  • __unm 对应的运算符 '-'.
  • __concat 对应的运算符 '..'.
  • __eq 对应的运算符 '=='.
  • __lt 对应的运算符 '<'.
  • __le 对应的运算符 '<='.


四)__call元方法
__call元方法在 Lua 调用一个值时调用。以下实例演示了计算两个表中所有值相加的和:

类似的 t();类似函数调用

local function call(mytable,newtable)
  local sum = 0
  local i
  for i = 1, table.maxn(mytable) do
    sum = sum + mytable[i]
  end
  for i = 1, table.maxn(newtable) do
    sum = sum + newtable[i]
  end
  return sum
end
local t1 = {1,2,3}
local t2 = {4,5,6}
setmetatable(t1,{__call = call})

local sum = t1(t2) 
print(sum)

 


五)__tostring 元方法
__tostring 元方法用于修改表的输出行为。以下实例我们自定义了表的输出内容,把表中所有的元素相加输出:

local t1 = {1,2,3}

setmetatable(t1,{
__tostring = function(mytable)
  local sum = 0
  for k, v in pairs(mytable) do
    sum = sum + v
  end
  return "all value sum =" .. sum

  end
})
print(t1) ----print方法会调用table的tostring元方法

 

到此我们的元表 和 元方法 就讲完了,这个是需要大家自己动手去测试体验的。要有领悟能力

 

六)点号与冒号操作符的区别

local str = "abcde"
print("case 1:", str:sub(1, 2))
print("case 2:", str.sub(str, 1, 2))

执行结果
case 1: ab
case 2: ab

 

冒号操作会带入一个 self 参数,用来代表 自己。而点号操作,只是 内容 的展开。
在函数定义时,使用冒号将默认接收一个 self 参数,而使用点号则需要显式传入 self 参数。

obj = { x = 20 }
function obj:fun1()
  print(self.x)
end
等价于
obj = { x = 20 }
function obj.fun1(self)
  print(self.x)
end

print(obj:func())
print(obj.fun2(obj))

 


注意:冒号的操作,只有当变量是类对象时才需要。

本文来自博客园,作者:孙龙-程序员,转载请注明原文链接:https://www.cnblogs.com/sunlong88/p/17950638

posted on 2024-01-07 15:41  孙龙-程序员  阅读(104)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3