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