Lua-matetable And matemethod

元表与元方法

1.元表

lua中每个值都有一个元表。table和userdata可以有各自独立的元表,而其他类型的值则共享其类型所属的单一元表。lua在创建新的table时不会创建元表:
任何table都可以作为任何值的元表,一组相关的table也可以共享一个通用的元表,此元表描述了他们共同的行为。一个table甚至可以作为自己的元表,描述其特有的行为。
在lua代码中,只能设置table的元表。若要设置其他类型的值的元表,则需要通过C代码来完成。

2.元方法

2.1 算术元方法

这里不过多赘述:
加法__add
乘法__mul
减法__sub
除法__div
相反数__unm
取模__mod
乘幂__pow
当2个值做以上的操作时,Lua会参照以下步骤来操作:
如果第一个值有元表,并且元表中有__add字段,那么Lua就以这个字段为元方法,而与第二个值无关;反之,如果第二个值有元表并含有__add字段,lua就以此字段为元方法;如果2个值都没有元方法,Lua就会引发一个错误。

2.2 关系元方法

类型:
等于__eq
小于__lt
小于等于__le
其他3个关系操作符没有单独的元方法,Lua会将~=转化为 not (a == b),其他同理
与算术元方法不同的是:关系元方法是不能应用于混合类型的。否则将引发一个错误。

2.3 库定义的元方法

类型:
__tostring
__metatable
到目前为止介绍的所有元方法都只针对Lua的核心,也就是一个虚拟机。他会检测一个操作中的值是否有元表,这些元表中是否定义了关于此操作的元方法。也可以说,由于元表也是常规的表,所以任何人,任何函数都可以使用他们。
getmetatable,和setmetatable是获取和设置元表,如果想要保护集合的元表,使用户既不能看也不能修改集合的元表。那么就需要用到字段__metatable。当设置了__metatable的值,getmetatable就会返回这个字段的值,而setmetatable则会引发一个错误。

2.4 table访问的元方法

算术类和关系类运算符的元方法都为各种错误情况定义了行为,它们不会改变语言的常规行为,但是Lua还提供了一种可以改变table行为的方法:查询table和修改table中不存在的字段。
__index:(上代码)

metaT = {}
metaT.p = {p1 =1, p2 = 2,p3 = 3,p4 = 4}
metaT.mt = {}

function metaT.new(t)
	setmetatable(t,metaT.mt)
	return t
end

metaT.mt.__index = function (table, key)
		return metaT.p[key]
end

w = metaT.new({p1 = 1,p2 = 2})
print(w.p3)  -------------> 3

当Lua检测到w中没有某个字段,但是其元表中却有一个__index,那么Lua就会以w这个table 和 "p3"来调用__index元方法。随后执行元方法内部内容。
__index元方法在继承中的使用非常常见,所以Lua提供了一个更简洁的使用方式,__index不一定非要赋值一个方法,也可以是一个表,当他是一个函数的时候,Lua将table和缺少的域作为参数调用这个函数;而他是一个表的时候,Lua将在这个表中看是否有这个域。所以上面的例子也可以写作

metaT.mt.__index = metaT.p

一个表来作为__index元方法使用,提供了一种简单的实现单继承的方式。一个函数的代价虽然高了点,单提供了更多的灵活性:我们实现多继承,隐藏,和其他一些变异的机制。

__newindex: 方法与__index方法类似,不同之处在于前者用于table的更新,而后者用于table的查询。当对一个table中不存在的索引赋值时,解释器就会查找__newindex元方法,如果有这个元方法,解释器就调用它,而不是执行赋值。如果这个元方法是一个table,解释器就在此table中执行赋值,而不是原来的table。此外还有一个原始函数允许绕过元方法:rawset(t,k,v)就可以不涉及任何元方法而直接设置table t中与key k相关联的value v。

Tab = {}
Tab.m = {}
Tab.por = {x = 100, y = 100, z = 100, width = 20, hight = 20}
function Tab.new(t)
    setmetatable(t,Tab.m)
    return t
end
--
Tab.m.__index = function (t,k)
    return Tab.por[k]
end
--
Tab.m.__newindex = function (t,k,v)
    Tab.por[k] = v
    print("------执行了__newIndex")
end

local tc = Tab.new{name = "box"}
print(tc.x)  -------100
 --先查找tc中是否有x字段,没有就查找是否有元表,有的话查找是否有__index方法,
--如果有且是一个table就直接取 ,
--如果是方法就直接执行方法内容,如果有返回值就返回对应的值,没有就返回一个nil。
tc.y=  20 -------就执行了赋值操作
tc.age = 20 --------   "------执行了__newIndex"
--当执行赋值操作时,如果此时当前表中(不包含元表) 就执行赋值操作,如果没有就去查找是否有元表,有的话查找是否有__newindex,
--如果有且是一个table就直接对table赋值,
--如果是方法就直接执行方法内容。不接受返回值。

2.5 具有默认值的table

2.6 跟踪table的访问

还是直接先上代码:

local _t = {}
TafterTab = {
    __index = function (t, k)
        print("这里查找了元素"..tostring(k))
        return _t[k]
    end,
    __newindex = function (t, k, v)
        print("这里更新了元素"..tostring(k))
        _t[k] = v
    end
}

function TafterTab.Tailafter(t)
    _t = t 
    t = {}
    setmetatable(t,TafterTab)
    return t;
end

将需要跟踪的目标表a的值全部搬运到另一个表b中,然后为目标表a设置一个元表c,并实现__index 和__newindex,方法中的查询和更新都是对表b去处理。这样所有的访问都会被__index和__newindex采集到。
这里的奥秘在于使用了“代理”的概念

2.7 只读table

老规矩~ 上菜!不对 上代码

function readOnly(t)
    -- body
    local proxy = {}
    local mt = {
        __index = t,
        __newindex = function (arg1, arg2, arg3)
            error("attempt to update a read-only table",2)
        end
    }
    setmetatable(proxy,mt)
    return proxy
end

days = readOnly{"Sunday","Monday","TuesDay"}
days[2] = "xxxx" ----"attempt to update a read-only table"

上面的代码不多废话啦,代理~
到此就over了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值