lua中每一個值都有一套預約義的操做集合,好比數字是能夠相加的,字符串是能夠鏈接的,可是對於兩個table類型,則不能直接進行「+」操做。這須要咱們進行一些操做。在lua中有一個元表(metatable),咱們能夠經過元表來改變一個值的行爲,使其在面對一個非預約義的操做時執行一個指定的操做。好比,如今有兩個table類型的變量a和b,咱們能夠經過metatable定義如何計算表達式a+b,具體的在Lua中是按照如下步驟進行的:函數
1.先判斷a和b二者之一是否有元表
lua
2.檢查該元表中是否有一個叫__add的字段
spa
3.若是找到該字段,就調用該字段對應的值,這個值對應的是一個metamethod(元方法)
prototype
4.調用__add對應的元方法計算a和b的值
code
在table中,咱們能夠從新定義的元方法有如下幾個:
索引
__add(a, b) --加法ip
__sub(a, b) --減法字符串
__mul(a, b) --乘法原型
__div(a, b) --除法string
__mod(a, b) --取模
__pow(a, b) --乘冪
__unm(a) --相反數
__concat(a, b) --鏈接
__len(a) --長度
__eq(a, b) --相等
__lt(a, b) --小於
__le(a, b) --小於等於
__index(a, b) --索引查詢
__newindex(a, b, c) --索引更新(PS:不懂的話,後面會有講)
__call(a, ...) --執行方法調用
__tostring(a) --字符串輸出
__metatable --保護元表
下面來進行一些個別的說明:
1.__add
local mytable = setmetatable({1,2,3},{ __add = function(mytable,newtable) for i=1,#newtable do table.insert(mytable,#mytable+1,newtable[i]) end return mytable end }) local secondtable = {4,5,6} mytable = mytable + secondtable for k,v in ipairs(mytable) do print(k,v) end
輸出爲:
1 1 2 2 3 3 4 4 5 5 6 6
注:因爲mytable和secondtable都是table,因此它們不能簡單的相加。這個時候,lua會去找mytable和secondtable,去看它們有沒有__add方法,這時候,lua發現mytable中有__add,所以調用了__add對應的函數,__add對應的函數傳入的第一個參數是mytable,第二個參數是secondtable。實際上就是至關於把secondtable的值都附加到mytable中。
如今你們有沒有想過,若是兩個table同時都有__add方法,那lua會執行哪一個呢?咱們看一下下面的代碼:
local mytable = setmetatable({1,2,3},{ __add = function(mytable,newtable) print("這是第一個table") for i=1,#newtable do table.insert(mytable,#mytable+1,newtable[i]) end return mytable end }) local secondtable = setmetatable({4,5,6},{ __add = function(mytable,newtable) print("這是第二個table") for i=1,#newtable do table.insert(mytable,#mytable+1,newtable[i]) end return mytable end }) mytable = secondtable + mytable + secondtable + mytable for k,v in ipairs(mytable) do print(k,v) end
輸出爲:
這是第二個table 這是第二個table 這是第二個table 1 4 2 5 3 6 4 1 5 2 6 3 7 4 8 5 9 6 10 1 11 2 12 3 13 1 14 2 15 3
注:由輸出咱們能夠知道,哪一個table在「+」操做最前面,則執行該table的__add方法,並且該語句後面也是都是調用第一個table的__add方法。還有朋友可能不太理解爲何會輸出1-15,不該該是1-12嗎?實際上是這樣的咱們在secondtable的__add方法已經把secondtable的值就行改變了,當他執行完secondtable + mytable這個的時候,它已經變成了4,5,6,1,2,3而mytable是在secondtable + mytable + secondtable+ mytable 執行完了以後才被賦值的,因此在右邊的兩個mytable都是1,2,3
接着咱們在看一下__index元方法,當訪問一個table中不存在的字段時,獲得的結果爲nil。這是對的,但並不是徹底正確。實際上,這些訪問會促使解釋器去查找一個叫__index的元方法。若是沒有這個元方法,那麼訪問結果如前述的胃nil。不然,就由這個元方法來提供最終結果。看下面的例子:
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) print("-------------------",key) for k,v in pairs(table) do print(k,v) end return window.prototype[key] end w = window.new{x=10,y=20} print(w.width,w.x,w.y,w.height)
輸出:
------------------- width y 20 x 10 ------------------- height y 20 x 10 100 10 20 100
注:lua檢測到w中沒有某字段,但在其元表中卻有一個__index字段,那麼lua就會以w(table)和"width"(不存在的key)來釣魚這個__index元方法。隨後元方法用這個key來索引原型table,並返回結果。__index元方法不必定是一個函數,它還能夠是一個table,當它是一個函數時,lua以table和不存在的key做爲參數來調用該函數,而當它是一個table時,lua就以相同的方式來從新訪問這個table(會在這個table中繼續查找)。
咱們如今來看一下__newindex元方法:
__newindex元方法於__index相似,__newindex用於table的更新,而__index用於table的查詢。當對一個table不存在的索引賦值時,解釋器就會查找__newindex元方法。若是有這個元方法,解釋器就調用它,而不是執行賦值。若是這個元方法是一個table,解釋器就在該table中執行賦值,而不是對原來的table。看下面的例子:
local man = { name = "大神", money = 30000, play = function() print("我去打籃球") end } local t = {} local mt = { __index = man, __newindex = function(table,key,value) print(key .. "不存在,不要嘗試給它賦值") end } setmetatable(t,mt) t.play = function() print("我去踢足球") end t.play()
輸出:
play不存在,不要嘗試給它賦值 我去打籃球
注:由輸出咱們能夠知道t.play = function() print("hi") end並無起到做用,這是由於給t的sayhello字段賦值的時候,lua判斷play字段不存在,因此會去調用元表裏的__newindex元方法。__newindex元方法被調用的時候會傳入3個參數:table自己,字段名,想要賦予的值。
咱們再看下面的例子:
local man = { name = "程序猿" } local updatetable = { name = "我是美男子" } local t = {} local mt = { __index = man, __newindex = updatetable } setmetatable(t,mt) print("updatetable賦值前:",updatetable.name) t.name = "我是大帥哥" print("updatetable賦值後:",updatetable.name) print("t.name:",t.name)
輸出:
updatetable賦值前: 我是美男子 updatetable賦值後: 我是大帥哥 t.name: 程序猿
這就是上面所說的:若是這個元方法是一個table,解釋器就在該table中執行賦值,而不是對原來的table。看下面的例子。
若是還不明白,在看下面的例子可能就會更加理解了:
mymetatable = {} mytable = setmetatable({key = "value"},{__newindex=mymetatable}) print(mytable.key) mytable.newkey = "newkey value" print(mytable.newkey,mymetatable.newkey) mytable.key = "key value" print(mytable.key,mymetatable.key)
輸出:
value nil newkey value key value nil
注:當lua執行mytable.newkey = "newkey value"這句時,會去mytable中查找newkey,發現沒有newkey的時候就會去找__newindex這個元方法,而後對__newindex對應的table進行賦值,所以mytable.newkey爲nil,mymetatable.newkey爲newkey value。而執行mytable.key = "key value"語句的時候,mytable存在key,所以只修改mytable中的key對應的值。
咱們再來看一下__call這個元方法,這個元方法會在Lua 調用一個值時調用,看下面的例子:
mytable = setmetatable({10},{ __call = function(mytable,newtable) sum = 0 for i=1,#mytable do sum = sum + mytable[i] end for i=1,#newtable do sum = sum + newtable[i] end return sum end }) newtable = {10,20,30} print(mytable(newtable))
輸出:
70
注:mytable是一個table,當調用其時(mytable(newtable))會執行__call這個元方法,第一個參數爲mytable,第二個參數爲newtable。
__tostring元方法,它主要是更改打印語句,看下面的例子:
mytable = setmetatable({},{ __tostring = function(mytable) return "我改變你的值" end }) print(mytable)
輸出:
我改變你的值