第一版是為 Lua 5.0 編寫的。雖然在很大程度上仍然與後續版本相關,但有一些差異。
第四版針對 Lua 5.3,可在 Amazon 和其他書店購買。
購買本書,您還可以 支援 Lua 專案。
![]() |
程式設計 Lua | ![]() |
第二部分。表格和物件 第 13 章。元表和元方法 |
在本節中,我們將介紹一個簡單的範例來說明如何使用元表。假設我們使用表格來表示集合,並使用函數來計算兩個集合的聯集、交集等。如同我們對清單所做的那樣,我們將這些函數儲存在表格中,並定義一個建構函式來建立新的集合
Set = {} function Set.new (t) local set = {} for _, l in ipairs(t) do set[l] = true end return set end function Set.union (a,b) local res = Set.new{} for k in pairs(a) do res[k] = true end for k in pairs(b) do res[k] = true end return res end function Set.intersection (a,b) local res = Set.new{} for k in pairs(a) do res[k] = b[k] end return res end為了協助檢查我們的範例,我們也定義一個函數來列印集合
function Set.tostring (set) local s = "{" local sep = "" for e in pairs(set) do s = s .. sep .. e sep = ", " end return s .. "}" end function Set.print (s) print(Set.tostring(s)) end
現在,我們想要讓加法運算子 (`+
´) 計算兩個集合的聯集。為此,我們將安排所有表示集合的表格共用一個元表,而這個元表將定義它們如何對加法運算子做出反應。我們的步驟是建立一個常規表格,我們將其用作集合的元表。為了避免污染我們的命名空間,我們將其儲存在 Set
表格中
Set.mt = {} -- metatable for sets下一步是修改建立集合的
Set.new
函數。新版本只有一個額外的程式行,它將 mt
設定為它所建立的表格的元表
function Set.new (t) -- 2nd version local set = {} setmetatable(set, Set.mt) for _, l in ipairs(t) do set[l] = true end return set end在那之後,我們使用
Set.new
建立的每個集合都將具有相同的表格作為其元表
s1 = Set.new{10, 20, 30, 50} s2 = Set.new{30, 1} print(getmetatable(s1)) --> table: 00672B60 print(getmetatable(s2)) --> table: 00672B60
最後,我們將所謂的元方法新增到元表,一個描述如何執行聯集的欄位 __add
Set.mt.__add = Set.union每當 Lua 嘗試新增兩個集合時,它將呼叫這個函數,並將兩個運算元作為引數。
有了元方法,我們可以使用加法運算子來執行集合聯集
s3 = s1 + s2 Set.print(s3) --> {1, 10, 20, 30, 50}類似地,我們可以使用乘法運算子來執行集合交集
Set.mt.__mul = Set.intersection Set.print((s1 + s2)*s1) --> {10, 20, 30, 50}
對於每個算術運算子,在元表中都有對應的欄位名稱。除了 __add
和 __mul
之外,還有 __sub
(用於減法)、__div
(用於除法)、__unm
(用於負數)和 __pow
(用於指數)。我們也可以定義 __concat
欄位,以定義串接運算子的行為。
當我們新增兩個集合時,無需質疑要使用哪個元表。但是,我們可以撰寫一個表達式,將具有不同元表的兩個值混合在一起,例如這樣
s = Set.new{1,2,3} s = s + 8要選擇元方法,Lua 會執行下列動作:(1) 如果第一個值具有包含
__add
欄位的元表,Lua 會使用這個值作為元方法,與第二個值無關;(2) 否則,如果第二個值具有包含 __add
欄位的元表,Lua 會使用這個值作為元方法;(3) 否則,Lua 會引發錯誤。因此,最後一個範例會呼叫 Set.union
,而表示式 10 + s
和 "hy" + s
也是如此。
Lua 對於這些混合型別不感興趣,但我們的實作會感興趣。如果我們執行 s = s + 8
範例,我們會在 Set.union
內部取得錯誤。
bad argument #1 to `pairs' (table expected, got number)如果我們想要更清楚的錯誤訊息,我們必須在嘗試執行操作之前明確檢查運算元的型別。
function Set.union (a,b) if getmetatable(a) ~= Set.mt or getmetatable(b) ~= Set.mt then error("attempt to `add' a set with a non-set value", 2) end ... -- same as before
版權所有 © 2003–2004 Roberto Ierusalimschy。保留所有權利。 | ![]() |