加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

Lua中的元表和元方法

发布时间:2020-12-14 22:16:28 所属栏目:大数据 来源:网络整理
导读:元表可以修改一个值的行为,使其在面对一个非预定义的操作时执行一个指定操作。 元方法会检测一个操作中的值是否有元表,以及元表是否定义了操作的元方法。如两个table相加,先检查两者之一是否有元表,之后检查是否有一个叫“__add”的字段,若找到,则调用

元表可以修改一个值的行为,使其在面对一个非预定义的操作时执行一个指定操作。
元方法会检测一个操作中的值是否有元表,以及元表是否定义了操作的元方法。如两个table相加,先检查两者之一是否有元表,之后检查是否有一个叫“__add”的字段,若找到,则调用对应的值。“__add”等字段,其对应的值(一般是一个函数或是table)就是“元方法”。

1、元表定义

元表是普通的Lua表。元表中的键为事件(event),称值为元方法(metamethod),通过设置特定的字段来改变作用于该值的操作的某些行为特征。

例如,当数字值作为加法的操作数时,Lua检查其元表中的"__add"字段是否有个函数。如果有,Lua调用它执行加法,其事件是"add",元方法是执行加法的函数。

2、元表的设置和获取

可通过函数getmetatable查询任何值的元表。

可通过函数setmetatable替换表的元表。?

1)元表设置元方法

例子1:

表加法的元方法的代码例子如下:

function add(t1,t2)
	--"#"运算符取表长度
	assert(#t1 == #t2)
	local length = #t1
	for i = 1,length do
		t1[i] = t1[i] + t2[i]
	end
	return t1
end


--setmetatable 设置表的元方法__add,支持表之间的加法,返回被设置的表
t1 = setmetatable({1,2,3},{__add = add})
t2 = setmetatable({10,20,30},{__add = add})

t1 = t1 + t2 -- 表之间相加,返回相加后的表
for i = 1,#t1 do
	print(t1[i])
end

输出结果:

11
22
33

例子2:

t = {}
print(getmetatable(t))  --显示过元表 此时是nil

--可以用setmetatable来设置或修改任何table的元表
t1 = {}
setmetatable(t,t1)
assert(getmetatable(t) == t1)

2)有元表的类型

任何table可以作为任何值的元表,而一组相关的table可以共享一个通用的元表,此元表描述了一个共同的行为。一个tabel甚至可以作为它自己的元表,用于描述其特有行为。在lua中,只能设置table的元表。要设置其它类型的元表,必须通过C代码来完成.

print(getmetatable("hi"))  --005DECD8 说明字符串有元表
print(getmetatable(10))  --nil  number没有元表

3)元表的索引表

__index表是作为提供查找方式的成员。

lua表的成员查找如下:

1.在表中查找,如果找到,返回该元素,找不到则继续

2.判断该表是否有元表,如果没有元表,返回nil,有元表则继续

3.判断元表有没有__index方法,如果__index方法为nil,则返回nil;

如果__index方法是一个表,则重复1、2、3;如果__index方法是一个函数,则返回该函数的返回值

方式1:

代码例子如下:

t = {}
m = {a = "and",b = "lilei",c= "han meiemei"}

setmetatable(t,{__index = m})--设置表的索引表
for k,v in pairs(t) do
	print(k,v)
end

print ("---------")
print (t.b,t.a,t.c)
输出结果:

---------
lilei and han meiemei

方式2:

father = {  
    house=1  
}  
father.__index = father -- 把father的__index方法指向自己  
son = {  
    car=1  
}  
setmetatable(son,father)  
print(son.house)  

输出的结果为1

如果father.__index 是空的,则找不到对应的元素

father = {  
    house=1  
}  
son = {  
    car=1  
}  
setmetatable(son,father) --把son的metatable设置为father  
print(son.house)  

输出的结果是nil

3、元表的元方法

元表可以控制对象的数学运算、顺序比较、连接、取长、和索引操作的行为。元表也能定义用户数据被垃圾收集时调用的函数。

Lua给这些操作的每一个都关联了事件的特定键。当Lua对某值执行其中一个操作时,检查该值是否含有元表以及相应的键(和对应的事件)。若有,则根据与该键关联的值(元方法)来完成操作。

每个操作由相应的名字标识。每个操作的键是由其名字前缀两个下划线“__”的字符串;例如,操作“加(add)”的键是字符串"__add"。

(1)获取给定对象的元方法

方式如下。下面显示的Lua代码只是说明性的;真实的行为被硬编码到解释器中,并且比这里的模拟更加高效。

metatable(obj)[event]

它应被解读为

rawget(getmetatable(obj) or {},event)

访问没有元表的对象或者该对象的元表没有该元方法,结果为nil。

(2)元方法的应用

1)并集和交集的元方法

Set = {}  --集合

local mt = {}  --集合元表

--根据参数列表中的值创建一个新的集合
function Set.new(l)
    local set = {}
    setmetatable(set,mt)  --指定 table set的元表为mt
    for k,v in ipairs(l) do
        set[v] = true    --注意,是拿索来当数据用的
    end
    return set
end
function Set.union(a,b)
    local res = Set.new{}
    for k,v in pairs(a) do res[k] = true end
    for k,v in pairs(b) do res[k] = true end
    return res
end

function Set.intersection(a,v in pairs(a) do
        if b[k] then
            res[k] = true--使用的是表res的键
        end
    end
    return res
end

function Set.tostring(set)
    local l = {}
    for k,v in pairs(set) do--把键依次添加到表中
        l[#l + 1] = k
    end
    return "{" .. table.concat(l,",") .. "}"
end

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


--将元方法加入元表
mt.__add = Set.union   --指定加号为求并集的方法
mt.__mul = Set.intersection  --指定乘号为交集的方法

s1 = Set.new({11,22,31,44,56})
s2 = Set.new({66,33,31})
s3 = s1 + s2 --求并集   
Set.print(s3) --输出 {11,66,56,44}
s4 = s1 * s2 --求交集
Set.print(s4) --输出 {31,22}

2)关系等元方法

关系是指 __eq(等于)、__lt(小于)等

mt.__le = function(a,b)--小于等于
     for k in pairs(a) do--a元素都在b中则为true,否则为false
        if not b[k] then  return false end
     end
     return true
end

mt.__lt = function(a,b)--小于
    return a<=b and not (b<=a)
end

mt.__eq = function(a,b)
    return a<=b and b<=a
end

ss1 = Set.new{2,4}
ss2 = Set.new{4,10,2}
print(ss1<=ss2)  --true
print(ss1<ss2)   --true
print(ss1>=ss1)  --true
print(ss1>ss1)   --false
print(ss1 == ss2*ss1)  --true


3)输出元方法

设置元表的__tostring字段

mt.__tostring = Set.tostring
打印集合
sstext = Set.new{33,55,6666}
print(sstext)  --{55,6666}

4)元表保护

假设想要保护的元表,使用户即不能看也不能修改的元表。那么就需要用到__metatable。当设置了该字段时,getmetatable就会返回这个字段的值,而setmetatable会引发一个错误

设置元表代码:

mt.__metatable = "not your business"

sstext1 = Set.new{}  
print(getmetatable(sstext1))  --not your business
setmetatable(s1,{})

5)元表__index的函数访问方式

通过元表的__index元方法触发函数,访问对象的成员

Window = {}

Window.prototype = {x=0,y=0,width = 100,height = 100}
Window.mt = {}

function Window.new(o)
    setmetatable(o,Window.mt)
    return o
end

--现在定义一个元方法
Window.mt.__index = function(table,key)--table 类似于隐藏参数this
    return Window.prototype[key]
end


w = Window.new({x=10,y=20})
print(w.width)  -- 100 实际上访问的是prototype的width字段,通过元表的__index元方法触发

6)元表的__newindex元方法

与__index不同的是__index是在查询的时候用的,而_newindex是在更新的时候用的

7)设置表默认值

通过元表设置表的默认值

function setDefault(t,d)
    local mt = {__index = function() return d end}
    setmetatable(t,mt)
end


8)通过元表设置表的代理

通过一个表的元表来提供方法访问另外一张表
local _t = {} --_t表是实际存储结构

t = {} --t表是访问代理

--元表mt是t表的元表(提供访问接口)
local mt = {
    __index = function(t,k)
        print("access to element "  .. tostring(k))
        return _t[k]
    end,__newindex = function(t,k,v)
        print("update of element " .. tostring(k) .. " to " .. tostring(v))
        _t[k] = v
    end
}
setmetatable(t,mt)

t[2]  = "hello"  --输出: update of element 2 to hello
print(t[2])  --输出: access to element 2

9)元表设置表的读权限

通过__newindex来限制表的修改权限,将8)中的代码__newindex元方法修改为:

__newindex = function(t,v)
    assert(0)--实际上没干事情
end

lua的参考手册:http://www.codingnow.com/2000/download/lua_manual.html

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读