LUA元表

lua元表和元方法 《lua程序设计》 13章 读书笔记

lua中每个值都有一个元表,talble和userdata可以有各自独立的元表,而其它类型的值则共享其类型所属的单一元表。lua在创建table时不会创建元表。

复制代码
t = {}

print(getmetatable(t)) --显示过元表 此时是nil --可以用setmetatable来设置或修改任何table的元表 t1 = {} setmetatable(t,t1) assert(getmetatable(t) == t1)
复制代码

任何table可以作为任何值的元表,而一组相关的table可以共享一个通用的元表,此元表描述了一个共同的行为。一个tabel甚至可以作为它自己的元表,用于描述其特有行为。

在lua中,只能设置table的元表。要设置其它类型的元表,必须通过C代码来完成

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

13.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,b) local res = Set.new{} for k,v in pairs(a) do if b[k] then res[k] = true 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,22,31} s3 = s1 + s2 --求并集 Set.print(s3) --输出 {11, 31, 66, 22, 33, 56, 44} s4 = s1 * s2 --求交集 Set.print(s4) --输出 {31, 22}
复制代码

13.2 关系类元方法

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

复制代码
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 = 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
复制代码

13.3 库定义的元方法

tostring是一个典型的实例。它能将各种类型的值表示为简单的文本格式

print({}) ----table: 003ECEF0

函数总是调用tostring来格式化输出。当格式化任意值时,tostring会检测该值是否有一个 __tostring元方法。如果有,他就调用这个方法用来作为tostring的返回值

在集合实例中,我们定议了将任命表示为字符串的方法,我们可以设置元表的__tostring字段

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

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

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

13.4 table 访问的元方法

13.4.1 __index元方法

当访问一个table中不存在的字段中时,如果这个字段不存在得到nil,但是如果这个table有一个元方法__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) return Window.prototype[key] end w = Window.new{x=10,y=20} print(w.width) -- 100 window实际上没有width这个字段
复制代码

__index元方法还可以是一个table

13.4.2  __newindex元方法

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

13.4.3具有默认值的table

以下代码为table设置默认值

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

13.4.4 跟踪table的访问

__index和__newindex都是在table中没有所需的index才发挥作用。因为只有table保持空才能捕捉到所有对他的访问,为了监视一个table的所有访问就得为真正的 table 创建一个代理

 

复制代码
t_src = {}  --要跟踪的表 local _t = t_src 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
复制代码

13.4.5 只读的table

只读table与上一节跟踪table类似,是通过__newindex来限制修改table内存

rawget是为了绕过__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 (t ,key)  

        return 1000  

    end  

    Window.mt.__newindex = function (table ,key ,value)  

        if key == "wangbin" then  

            rawset(table ,"wangbin" ,"yes,i am")  

        end  

    end  

    w = Window.new{x = 10 ,y = 20}  

    print(rawget(w ,w.wangbin))  

打印结果是:nil。这里的元表中__index函数就不再起作用了。

 

但是rawset呢,起什么作用呢?我们再来运行一段代码。

    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 (t ,key)  

        return 1000  

    end  

    Window.mt.__newindex = function (table ,key ,value)  

        table.key = "yes,i am"  

    end  

    w = Window.new{x = 10 ,y = 20}  

    w.wangbin = "55"  

然后我们的程序就stack overflow了。可见,程序陷入了死循环。因为w.wangbin这个元素本来就不存在表中,然后这里不断执行进入__newindex,陷入了死循环。

你可能感兴趣的:(lua)