引言
在上篇博客中,我們簡單地學習了一下Lua的基本語法。其實在Lua中有一個還有一個叫元表的概念,不得不着重地探討一下。元表在實際地開發中,也是會被極大程度地所使用到。本篇博客,就讓我們從Lua查找表元素的過程,來探討學習一下Lua中的元表。
一、什么是元表
在Lua table中我們可以訪問對應的key來得到value值,但是卻無法對兩個table進行操作。因此Lua 提供了元表(Metatable),允許我們改變table的行為,每個行為關聯了對應的元方法。通俗來說,元表就像是一個“操作指南”,里面包含了一系列操作的解決方案,例如__index方法就是定義了這個表在索引失敗的情況下該怎么辦,__add方法就是告訴table在相加的時候應該怎么做。這里面的__index,__add就是元方法,下面我們詳細解讀一下元方法。
二、什么是元方法
通過上面的知識,我們知道了通過使用元表可以定義Lua如何計算兩個table的相加操作。當Lua試圖對兩個表進行相加時,先檢查兩者之一是否有元表,之后檢查是否有一個叫"__add"的字段,若找到,則調用對應的值。"__add"等即時字段,其對應的值(往往是一個函數或是table)就是"元方法"。很多人對Lua中的元表和元方法都會有一個這樣的誤解:“如果A的元表是B,那么如果訪問了一個A中不存在的成員,就會訪問查找B中有沒有這個成員”。如果說這樣去理解的話,就大錯特錯了,實際上即使將A的元表設置為B,而且B中也確實有這個成員,返回結果仍然會是nil,原因就是B的__index元方法沒有賦值。別忘了我們之前說過的:“元表是一個操作指南”,定義了元表,只是有了操作指南,但不應該在操作指南里面去查找元素,而__index方法則是“操作指南”的“索引失敗時該怎么辦”。下面我們通過幾段實際的代碼來看一下Lua的表元素的查找過程以便更深入地體會上述這些概念。
下面是一些Lua表中可以重新定義的元方法:
__add(a, b) --加法 __sub(a, b) --減法 __mul(a, b) --乘法 __div(a, b) --除法 __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 --保護元表
三、Lua的表元素查找機制
眾所周知,Lua的表本質其實是個類似Dictionary的東西,其元素是很多的Key-Value鍵值對。如果嘗試訪問了一個表中並不存在的元素時,就會觸發Lua的一套查找機制,Lua也是憑借這個機制來模擬了類似“類”的行為。下面是一段簡單地訪問表中元素的代碼:
myTable = { prop1 = 'Property', } print (myTable.prop1) print (myTable.prop2) --打印不存在的成員prop2
稍微有些Lua語法的同學,一看就可以看出,上面的輸出結果為:Property nil 。輸出為nil的原因很簡單,myTable中並沒有prop2這個成員,這符合我們平時操作Dictionary的習慣。但對於Lua的表,如果myTable有元表和元方法,情況就不同了。下面我們再看一下設置了元表和元方法的代碼:
father = { prop1=1 } son = { prop2=1 } setmetatable(son, father) --把son的metatable設置為father print (son.prop1)
執行輸出的結果仍然為:nil,這正印證了上面所說的,只設置元表是不管用的。再來看看同時設置元表和對應的元方法的代碼:
father = { prop1=1 } father.__index = father -- 把father的__index方法指向它本身 son = { prop2=1 } setmetatable(son, father) --把son的metatable設置為father print (son.prop1)
執行輸出的結果為:1。
結合上述的幾個小例子,我們再來解釋一下__index元方法的含義:在上面的例子中,當訪問son.prop1時,son中是沒有prop1這個成員的。接着Lua解釋器發現son設置了元表:father,(需要注意的是:此時Lua並不是直接在fahter中找到名為prop1的成員,而是先調用father的__index方法),如果__index方法為nil,則直接返回nil。如果__index指向了一張表(上面的例子中father的__index指向了自己本身),那么就會到__index方法所指向的這個表中去查找名為prop1的成員。最終,我們在father表中找到了prop1成員。這里的__index方法除了可以是一個表,也可以是一個函數,如果是函數的話,__index方法被調用時會返回該函數的返回值。
Lua查找一個表元素的規則可以歸納為如下幾個步驟:
- Step1:在表自身中查找,如果找到了就返回該元素,如果沒找到則執行Step2;
- Step2:判斷該表是否有元表(操作指南),如果沒有元表,則直接返回nil,如果有元表則繼續執行Step3;
- Step3:判斷元表是否設置了有關索引失敗的指南(__index元方法),如果沒有(__index為nil),則直接返回nil;如果有__index方法是一張表,則重復執行Step1->Step2->Step3;如果__index方法是一個函數,則返回該函數的返回值
作者:馬三小伙兒
出處:http://www.cnblogs.com/msxh/p/7745553.html
請尊重別人的勞動成果,讓分享成為一種美德,歡迎轉載。另外,文章在表述和代碼方面如有不妥之處,歡迎批評指正。留下你的腳印,歡迎評論!