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內存


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM