元表是用來定義對table或userdata操做方式的表編程
local t1 = {1} local t2 = {2} local t3 = t1 + t2
咱們直接對兩個table執行+運算,會報錯函數
lua: /usercode/file.lua:3: attempt to perform arithmetic on local 't1' (a table value)
由於程序不知道如何對兩個表執行+運行,這時候就須要經過元表來定義如何執行t1的+運算,有點相似於c語言中的運算符重載。lua
local mt = {} --定義mt.__add元方法(其實就是元表中一個特殊的索引值)爲將兩個表的元素合併後返回一個新表 mt.__add = function(t1,t2) local temp = {} for _,v in pairs(t1) do table.insert(temp,v) end for _,v in pairs(t2) do table.insert(temp,v) end return temp end local t1 = {1,2,3} local t2 = {2} --設置t1的元表爲mt setmetatable(t1,mt) local t3 = t1 + t2 --輸出t3 local st = "{" for _,v in pairs(t3) do st = st..v..", " end st = st.."}" print(st)
結果爲:code
{1, 2, 3, 2, }
由於程序在執行t1+t2的時候,會去調用t1的元表mt的__add元方法進行計算。
具體的過程是:
1.查看t1是否有元表,如有,則查看t1的元表是否有__add元方法,如有則調用。
2.查看t2是否有元表,如有,則查看t2的元表是否有__add元方法,如有則調用。
3.若都沒有則會報錯。
因此說,咱們經過定義了t1元表的__add元方法,達到了讓兩個表經過+號來相加的效果orm
函數 | 描述 |
---|---|
__add | 運算符 + |
__sub | 運算符 - |
__mul | 運算符 * |
__ div | 運算符 / |
__mod | 運算符 % |
__unm | 運算符 -(取反) |
__concat | 運算符 .. |
__eq | 運算符 == |
__lt | 運算符 < |
__le | 運算符 <= |
__call | 當函數調用 |
__tostring | 轉化爲字符串 |
__index | 調用一個索引 |
__newindex | 給一個索引賦值 |
因爲那幾個運算符使用相似,因此就不單獨說明了,接下來講 __call, __tostring, __index, __newindex四個元方法。對象
__call可讓table當作一個函數來使用。索引
local mt = {} --__call的第一參數是表本身 mt.__call = function(mytable,...) --輸出全部參數 for _,v in ipairs{...} do print(v) end end t = {} setmetatable(t,mt) --將t看成一個函數調用 t(1,2,3)
結果:ip
1 2 3
__tostring能夠修改table轉化爲字符串的行爲字符串
local mt = {} --參數是表本身 mt.__tostring = function(t) local s = "{" for i,v in ipairs(t) do if i > 1 then s = s..", " end s = s..v end s = s .."}" return s end t = {1,2,3} --直接輸出t print(t) --將t的元表設爲mt setmetatable(t,mt) --輸出t print(t)
結果:get
table: 0x14e2050 {1, 2, 3}
調用table的一個不存在的索引時,會使用到元表的__index元方法,和前幾個元方法不一樣,__index能夠是一個函數也但是一個table。
做爲函數:
將表和索引做爲參數傳入__index元方法,return一個返回值
local mt = {} --第一個參數是表本身,第二個參數是調用的索引 mt.__index = function(t,key) return "it is "..key end t = {1,2,3} --輸出未定義的key索引,輸出爲nil print(t.key) setmetatable(t,mt) --設置元表後輸出未定義的key索引,調用元表的__index函數,返回"it is key"輸出 print(t.key)
結果:
nil it is key
做爲table:
查找__index元方法表,如有該索引,則返回該索引對應的值,不然返回nil
local mt = {} mt.__index = {key = "it is key"} t = {1,2,3} --輸出未定義的key索引,輸出爲nil print(t.key) setmetatable(t,mt) --輸出表中未定義,但元表的__index中定義的key索引時,輸出__index中的key索引值"it is key" print(t.key) --輸出表中未定義,但元表的__index中也未定義的值時,輸出爲nil print(t.key2)
結果:
nil it is key nil
當爲table中一個不存在的索引賦值時,會去調用元表中的__newindex元方法
做爲函數
__newindex是一個函數時會將賦值語句中的表、索引、賦的值看成參數去調用。不對錶進行改變
local mt = {} --第一個參數時表本身,第二個參數是索引,第三個參數是賦的值 mt.__newindex = function(t,index,value) print("index is "..index) print("value is "..value) end t = {key = "it is key"} setmetatable(t,mt) --輸出表中已有索引key的值 print(t.key) --爲表中不存在的newKey索引賦值,調用了元表的__newIndex元方法,輸出了參數信息 t.newKey = 10 --表中的newKey索引值仍是空,上面看着是一個賦值操做,其實只是調用了__newIndex元方法,並無對t中的元素進行改動 print(t.newKey)
結果:
it is key index is newKey value is 10 nil
做爲table
__newindex是一個table時,爲t中不存在的索引賦值會將該索引和值賦到__newindex所指向的表中,不對原來的表進行改變。
local mt = {} --將__newindex元方法設置爲一個空表newTable local newTable = {} mt.__newindex = newTable t = {} setmetatable(t,mt) print(t.newKey,newTable.newKey) --對t中不存在的索引進行負值時,因爲t的元表中的__newindex元方法指向了一個表,因此並無對t中的索引進行賦值操做將,而是將__newindex所指向的newTable的newKey索引賦值爲了"it is newKey" t.newKey = "it is newKey" print(t.newKey,newTable.newKey)
結果:
nil nil nil it is newKey
有時候咱們但願直接改動或獲取表中的值時,就須要rawget和rawset方法了。
rawget可讓你直接獲取到表中索引的實際值,而不經過元表的__index元方法。
local mt = {} mt.__index = {key = "it is key"} t = {} setmetatable(t,mt) print(t.key) --經過rawget直接獲取t中的key索引 print(rawget(t,"key"))
結果:
it is key nil
rawset可讓你直接爲表中索引的賦值,而不經過元表的__newindex元方法。
local mt = {} local newTable = {} mt.__newindex = newTable t = {} setmetatable(t,mt) print(t.newKey,newTable.newKey) --經過rawset直接向t的newKey索引賦值 rawset(t,"newKey","it is newKey") print(t.newKey,newTable.newKey)
結果:
nil nil it is newKey nil
經過爲table設置元表能夠在lua中實現面向對象編程。
經過對userdata和元表能夠實如今lua中對c中的結構進行面向對象式的訪問。