Lua中關(guān)于元表和元方法學(xué)習(xí)教程
Lua中關(guān)于元表和元方法學(xué)習(xí)是本文要介紹的內(nèi)容,主要來了解Lua中每個(gè)值都可具有元表。 元表是普通的Lua表,定義了原始值在某些特定操作下的行為。你可通過在值的原表中設(shè)置特定的字段來改變作用于該值的操作的某些行為特征。例如,當(dāng)數(shù)字值作為加法的操作數(shù)時(shí),Lua檢查其元表中的"__add"字段是否有個(gè)函數(shù)。如果有,Lua調(diào)用它執(zhí)行加法。
我們稱元表中的鍵為事件(event),稱值為元方法(metamethod)。前述例子中的事件是"add",元方法是執(zhí)行加法的函數(shù)。
可通過函數(shù)getmetatable查詢?nèi)魏沃档脑怼?/p>
可通過函數(shù)setmetatable替換表的元表。不能從Lua中改變其他類型的元表(除了使用調(diào)試庫);必須使用C API才能做到。
表和完整的用戶數(shù)據(jù)具有獨(dú)立的元表(盡管多個(gè)表和用戶數(shù)據(jù)可共享元表);每種其他類型的所有值共享一個(gè)元表。所以,所有數(shù)字共享一個(gè)元表,字符串也是,等等。
元表可以控制對(duì)象的數(shù)學(xué)運(yùn)算、順序比較、連接、取長、和索引操作的行為。元表也能定義用戶數(shù)據(jù)被垃圾收集時(shí)調(diào)用的函數(shù)。Lua給這些操作的每一個(gè)都關(guān)聯(lián)了稱為事件的特定鍵。當(dāng)Lua對(duì)某值執(zhí)行其中一個(gè)操作時(shí),檢查該值是否含有元表以及相應(yīng)的事件。如果有,與該鍵關(guān)聯(lián)的值(元方法)控制Lua如何完成操作。
元表控制后面列舉的操作。每個(gè)操作由相應(yīng)的名字標(biāo)識(shí)。每個(gè)操作的鍵是由其名字前綴兩個(gè)下劃線“__”的字符串;例如,操作“加(add)”的鍵是字符串"__add"。這些操作的語義通過一個(gè)Lua函數(shù)描述解釋器如何執(zhí)行操作作了更好的說明。
下面顯示的Lua代碼只是說明性的;真實(shí)的行為被硬編碼到解釋器中,并且比這里的模擬更加高效。這些描述中的所有函數(shù)(rawget、tonumber等等。)在§5.1中描述。特別一提,要獲取給定對(duì)象的元方法,我們使用表達(dá)式
- metatable(obj)[event]
它應(yīng)被解讀為
- rawget(getmetatable(obj) or {}, event)
就是說,訪問一個(gè)元方法不會(huì)調(diào)用其他元方法,而且訪問沒有元表的對(duì)象不會(huì)失?。ㄖ皇墙Y(jié)果為nil)。
"add": + 操作。
下面的getbinhandler函數(shù)定義Lua如何選擇二元操作的處理程序。首先嘗試***操作數(shù),如果它的類型沒有定義該操作的處理程序,則嘗試第二操作數(shù)。
- function getbinhandler (op1, op2, event)
- return metatable(op1)[event] or metatable(op2)[event]
- end
通過應(yīng)用該函數(shù),op1 + op2的行為是
- function add_event (op1, op2)
- local o1, o2 = tonumber(op1), tonumber(op2)
- if o1 and o2 then -- 兩操作數(shù)都是數(shù)字
- return o1 + o2 -- ‘+’此處是‘add’的原語
- else -- 至少一個(gè)操作數(shù)不是數(shù)字
- local h = getbinhandler(op1, op2, "__add")
- if h then -- 用兩個(gè)操作數(shù)調(diào)用處理程序
- return (h(op1, op2))
- else -- 沒有可用的處理程序:缺省行為
- error(...)
- end
- end
- end
- "sub": - 操作。 行為類似于“add”操作。
- "mul": * 操作。 行為類似于“add”操作。
- "div": / 操作。 行為類似于“add”操作。
- "mod": % 操作。 行為類似于“add”操作。以o1 - floor(o1/o2)*o2為操作原語。
- "pow": ^ (取冪)操作。 行為類似于“add”操作,以函數(shù)pow(來自C數(shù)學(xué)庫)為操作原語。
- "unm": 一元-操作。
- function unm_event (op)
- local o = tonumber(op)
- if o then -- 操作數(shù)是數(shù)字?
- return -o -- ‘-’此處是‘unm’的原語
- else -- 操作數(shù)不是數(shù)字
- -- 嘗試由操作數(shù)取得處理程序。
- local h = metatable(op).__unm
- if h then-- 用操作數(shù)調(diào)用處理程序
- return (h(op))
- else -- 沒有可用的處理程序:缺省行為
- error(...)
- end
- end
- end
"concat": .. (連接)操作。
- function concat_event (op1, op2)
- if (type(op1) == "string" or type(op1) == "number") and
- (type(op2) == "string" or type(op2) == "number") then
- return op1 .. op2 -- 字符串連接原語
- else
- local h = getbinhandler(op1, op2, "__concat")
- if h then
- return (h(op1, op2))
- else
- error(...)
- end
- end
- end
"len": # 操作。
- function len_event (op)
- if type(op) == "string" then
- return strlen(op) -- 取字符串長度原語
- elseif type(op) == "table" then
- return #op -- 取表長度原語
- else
- local h = metatable(op).__len
- if h then -- 用操作數(shù)調(diào)用處理程序
- return (h(op))
- else -- 沒有可用的處理程序:缺省行為
- error(...)
- end
- end
- end
"eq": == 操作。 函數(shù)getcomphandler定義Lua如何選擇比較操作符的元方法。只有待比較的兩個(gè)對(duì)象類型和選定操作對(duì)應(yīng)的元方法都相同,才會(huì)選擇該元方法。
- function getcomphandler (op1, op2, event)
- if type(op1) ~= type(op2) then return nil end
- local mm1 = metatable(op1)[event]
- local mm2 = metatable(op2)[event]
- if mm1 == mm2 then
- return mm1
- else
- return nil
- end
- end
- "eq"事件定義如下:
- function eq_event (op1, op2)
- if type(op1) ~= type(op2) then -- 類型不同?
- return false -- 對(duì)象不同
- end
- if op1 == op2 then -- 相等原語?
- return true -- 對(duì)象相同
- end -- 嘗試元方法
- local h = getcomphandler(op1, op2, "__eq")
- if h then
- return (h(op1, op2))
- else
- return false
- end
- end
- a ~= b等價(jià)于not (a == b)。
- "lt": < 操作。
- function lt_event (op1, op2)
- if type(op1) == "number" and type(op2) == "number" then
- return op1 < op2 -- 數(shù)字比較
- elseif type(op1) == "string" and type(op2) == "string" then
- return op1 < op2 -- 詞典順序比較
- else
- local h = getcomphandler(op1, op2, "__lt")
- if h then
- return (h(op1, op2))
- else
- error(...);
- end
- end
- end
- a > b等價(jià)于b < a。
- "le": <= 操作。
- function le_event (op1, op2)
- if type(op1) == "number" and type(op2) == "number" then
- return op1 <= op2 -- 數(shù)字比較
- elseif type(op1) == "string" and type(op2) == "string" then
- return op1 <= op2 -- 詞典順序比較
- else
- local h = getcomphandler(op1, op2, "__le")
- if h then
- return (h(op1, op2))
- else
- h = getcomphandler(op1, op2, "__lt")
- if h then
- return not h(op2, op1)
- else
- error(...);
- end
- end
- end
- end
a >= b等價(jià)于 b <= a。注意,假定a <= b等價(jià)于not (b < a),那么當(dāng)沒有“le”元方法時(shí),Lua嘗試“lt”。
- "index": 索引訪問table[key]。
- function gettable_event (table, key)
- local h
- if type(table) == "table" then
- local v = rawget(table, key)
- if v ~= nil then
- return v
- end
- h = metatable(table).__index
- if h == nil then
- return nil
- end
- else
- h = metatable(table).__index
- if h == nil then
- error(...);
- end
- end
- if type(h) == "function" then
- return (h(table, key)) -- 調(diào)用處理程序
- else
- return h[key] -- 對(duì)它重復(fù)上述操作
- end
- end
- "newindex": 索引賦值table[key] = value。
- function settable_event (table, key, value)
- local h
- if type(table) == "table" then
- local v = rawget(table, key)
- if v ~= nil then
- rawset(table, key, value);
- return
- end
- h = metatable(table).__newindex
- if h == nil then
- rawset(table, key, value);
- return
- end
- else
- h = metatable(table).__newindex
- if h == nil then
- error(...);
- end
- end
- if type(h) == "function" then
- h(table, key,value) -- 調(diào)用處理程序
- else
- h[key] = value -- 對(duì)它重復(fù)上述操作
- end
- end
- "call": 當(dāng)Lua調(diào)用值時(shí)被調(diào)用。
- function function_event (func, ...)
- if type(func) == "function" then
- return func(...) -- 調(diào)用原語
- else
- local h = metatable(func).__call
- if h then
- return h(func, ...)
- else
- error(...)
- end
- end
- end
小結(jié):Lua中關(guān)于元表和元方法學(xué)習(xí)教程的內(nèi)容介紹完了,希望通過本文的學(xué)習(xí)能對(duì)你有所幫助!