標籤:
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 setendfunction 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 resendfunction 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 resendfunction Set.tostring(set) local l = {} for k,v in pairs(set) do l[#l + 1] = k end return "{" .. table.concat(l,", ") .. "}"endfunction 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 trueendmt.__lt = function(a,b) return a<=b and not (b<=a)endmt.__eq = function(a,b) return a<=b and b<=aendss1 = Set.new{2,4}ss2 = Set.new{4,10,2}print(ss1<=ss2) --trueprint(ss1<ss2) --trueprint(ss1>=ss1) --trueprint(ss1>ss1) --falseprint(ss1 == ss2*ss1) --true13.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 businesssetmetatable(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 oend--現在定義一個元方法Window.mt.__index = function(table,key) return Window.prototype[key]endw = 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)end13.4.4 跟蹤table的訪問
__index和__newindex都是在table中沒有所需的index才發揮作用。因為只有table保持空才能捕捉到所有對他的訪問,為了監視一個table的所有訪問就得為真正的 table 建立一個代理
t_src = {} --要跟蹤的表local _t = t_srct = {} --建立代理--建立元表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 helloprint(t[2]) --*access to element 213.4.5 唯讀table
唯讀table與上一節跟蹤table類似,是通過__newindex來限制修改table記憶體
[轉]LUA元表