openresty開發系列22--lua的元表

openresty開發系列22--lua的元表


舉個例子,在 Lua table 中咱們能夠訪問對應的key來獲得value值,可是卻沒法對兩個 table 進行操做。

那如何計算兩個table的相加操做a+b?

local t1 = {1,2,3}
local t2 = {4,5,6}

local t3 = t1 + t2   ---->  {1,2,3,4,5,6}

相似java的一些操做重載

這種相似的需求,lua 提供了元表(Metatable)和元方法,容許咱們改變table的行爲,每一個行爲關聯了對應的元方法。

1)setmetatable(table,metatable): 對指定table設置元表(metatable),
若是元表(metatable)中存在__metatable鍵值,setmetatable會失敗 。

2)getmetatable(table): 返回對象的元表(metatable)。

mytable = {}                          -- 普通表
mymetatable = {}                      -- 元表
setmetatable(mytable,mymetatable)     -- 把 mymetatable 設爲 mytable 的元表

等價於:
mytable = setmetatable({},{})

返回對象元表:
getmetatable(mytable)                 -- 返回mymetatable


元方法的命名都是以 __ 兩個下劃線開頭。

一)__index 元方法
對錶讀取索引一個元方法

這是 metatable 最經常使用的鍵。

當你經過鍵來訪問 table 的時候,若是這個鍵沒有值,那麼Lua就會尋找該table的metatable(假定有metatable)
中的__index元方法。若是__index是一個表,Lua會在表格中查找相應的鍵。

local t = {}              -- 普通表t爲空
local other = {foo = 2}   -- 元表 中有foo值
setmetatable(t,{__index = other})     -- 把 other 設爲 t 的元表__index
print(t.foo);  ---輸出 2
print(t.bar);  ---輸出nil

t.foo爲何會輸出2,就是由於咱們重寫了__index索引的重載,lua在執行中若是t中沒有foo,
就會在他的元表中__index中去找,__index等於other,就輸出2。

----------------

若是咱們把t設置一下foo的值爲3,在看看結果
local t = {foo = 3 }                  -- 普通表t爲空
local other = {foo = 2}               -- 元表 中有foo值
setmetatable(t,{__index = other})     -- 把 other 設爲 t 的元表__index
print(t.foo);  ---輸出 3
print(t.bar);  ---輸出nil

---------------------------------------------

若是__index賦值爲一個函數的話,Lua就會調用那個函數,table和鍵會做爲參數傳遞給函數。
__index 元方法查看錶中元素是否存在,若是不存在,返回結果爲 nil;若是存在則由 __index 返回結果。

local t = {key1 = "value1" }            
local function metatable(mytable,key)
   if key == "key2" then
      return "metatablevalue"
   else
      return nil
   end
end
setmetatable(t,{__index = metatable})     
print(t.key1);
print(t.key2);  
print(t.key3);  

分析:

print(t.key1);  ---這個輸出value1 ,是由於t表中有此key
print(t.key2);  ---這個輸出metatablevalue,是由於t表中沒有此key,就會調用t的元表中的元方法__index,
                ---這是__index是個函數,就會執行這個函數,傳t表和key值這兩個參數到此函數,
                ---函數體中判斷有此key2 就輸出metatablevalue;

print(t.key3);  ---這個輸出nil,是由於t表沒有,元方法__index函數中 對key3返回nil值

--------------------------------------

總結:lua對錶索引取值的步驟

Lua查找一個表元素時的規則,其實就是以下3個步驟:
1.在表中查找,若是找到,返回該元素,找不到則繼續步驟2
2.判斷該表是否有元表,若是沒有元表,返回nil,有元表則繼續步驟3。
3.判斷元表有沒有__index元方法,若是__index方法爲nil,則返回nil;
  若是__index方法是一個表,則重複一、二、3;
  若是__index方法是一個函數,則執行該函數,獲得返回值。


二)__newindex 元方法

__newindex 元方法用來對錶更新,__index則用來對錶訪問 。

當你給表進行索引進行賦值,但此索引不存在;lua會查找是否有元表,有元表就會查找__newindex 元方法是否存在:
若是存在則調用__newindex的值進行執行操做,但不會對原表進行賦值操做。

如下實例演示了 __newindex 元方法的應用:

mymetatable = {}
mytable = setmetatable({key1 = "value1"}, { __newindex = mymetatable })

print("mytable.key1=",mytable.key1)
mytable.newkey = "新值2"

print("mytable.newkey=",mytable.newkey)
print("mymetatable.newkey=",mymetatable.newkey)

mytable.key1 = "新值1"
print("mytable.key1=",mytable.key1)
print("mymetatable.key1=",mymetatable.key1)

以上實例中表設置了元方法 __newindex
在對新索引鍵(newkey)賦值時(mytable.newkey = "新值2"),會調用元方法,而對mytable原表不進行賦值。
而對mytable已存在的索引鍵(key1),則會進行賦值,而不調用元方法 __newindex。

----------------------------

若是咱們要對原來的table進行賦值,那咱們就能夠用rawset;;__newindex函數會傳三個參數,
mytable = setmetatable({key1 = "value1"}, {
    __newindex = function(t,k,v)    ---第一個參數爲table,第二個參數爲key,第三個參數爲value
        rawset(t,k,v);
    end
})
mytable.key1 = "new value"
mytable.key2 = 4
print("mytable.key1=",mytable.key1)
print("mytable.key2=",mytable.key2)

key2本來是再也不mytable表中的,經過元方法__newindex中函數使用了rawset,就能夠對原table進行賦值。

三)爲表添加操做符「+」

咱們這裏定義「+」這元方法,把它定義爲兩個table相連

t1={1,2,3}  
t2={4,5,6}
t1 + t2 相加的結果,咱們想獲得的是 {1,2,3,4,5,6}
那咱們如何寫元表?
「+」對應的元方法爲__add

local function add(mytable,newtable)
    local num = table.maxn(newtable)
    for i = 1, num do
      table.insert(mytable,newtable[i])
  end
  return mytable
end

local t1 = {1,2,3}
local t2 = {4,5,6}

setmetatable(t1,{__add = add})

t1 = t1 + t2

for k,v in ipairs(t1) do
    print("key=",k," value=",v)
end

這樣咱們就實現了兩個table相加


如下是咱們的操做符對應關係
模式                    描述
__add                 對應的運算符 '+'.
__sub                 對應的運算符 '-'.
__mul                 對應的運算符 '*'.
__div                 對應的運算符 '/'.
__mod                 對應的運算符 '%'.
__unm                 對應的運算符 '-'.
__concat              對應的運算符 '..'.
__eq                  對應的運算符 '=='.
__lt                  對應的運算符 '<'.
__le                  對應的運算符 '<='.


四)__call元方法
__call元方法在 Lua 調用一個值時調用。如下實例演示了計算兩個表中全部值相加的和:

相似的 t();相似函數調用

local function call(mytable,newtable)
    local sum = 0
    local i
    for i = 1, table.maxn(mytable) do
        sum = sum + mytable[i]
    end
    for i = 1, table.maxn(newtable) do
        sum = sum + newtable[i]
    end
    return sum
end
local t1 = {1,2,3}
local t2 = {4,5,6}
setmetatable(t1,{__call = call})

local sum = t1(t2)     
print(sum)


五)__tostring 元方法
__tostring 元方法用於修改表的輸出行爲。如下實例咱們自定義了表的輸出內容,把表中全部的元素相加輸出:
local t1 = {1,2,3}

setmetatable(t1,{
    __tostring = function(mytable)
        local sum = 0
        for k, v in pairs(mytable) do
            sum = sum + v
        end
        return "all value sum =" .. sum
    
    end
})
print(t1)    ----print方法會調用table的tostring元方法   

到此咱們的元表 和 元方法 就講完了,這個是須要你們本身動手去測試體驗的。要有領悟能力



六)點號與冒號操做符的區別

local str = "abcde"
print("case 1:", str:sub(1, 2))
print("case 2:", str.sub(str, 1, 2))

執行結果
case 1: ab
case 2: ab

冒號操做會帶入一個 self 參數,用來表明 本身。而點號操做,只是 內容 的展開。
在函數定義時,使用冒號將默認接收一個 self 參數,而使用點號則須要顯式傳入 self 參數。
obj = { x = 20 }
function obj:fun1()
    print(self.x)
end
等價於
obj = { x = 20 }
function obj.fun1(self)
    print(self.x)
end
注意:冒號的操做,只有當變量是類對象時才須要。

java

相關文章
相關標籤/搜索