Metatables 允許桌子變得比以前更強大。它們是隨機數據附加到桌子上,並且包含名為 metamethod 的值。Metatables 會在某些操作使用與桌子隨機數據附加時發生。
考慮以下代碼:
local list = {1, 2}print(list[3])
你可能會期望這個代碼在列表中尋找第三個索引,並且找到沒有什麼,然後返回零。但這不正確。 其實,代碼在列表中尋找第三個索引,並且找到沒有什麼,然後檢查是否有附加的 metatable 在表上,並將零返回。 如果沒有附加的 metatable 在表上,則會返回 nil。
操作元桌
新增和查找桌子的 mettab 的兩個主要功能是 setmetatable 和 getmetatable
local x = {}local metaTable = {} -- 也有 metaTables 是桌子!setmetatable(x, metaTable) -- 提供一個名為 metaTable 的擴展表!print(getmetatable(x)) --> table: [hexadecimal memory address]
setmetatable 函數還返回你設置 metatable 的表,因此這兩個指令碼都做相同的事:
local x = {}setmetatable(x, {})
local x = setmetatable({}, {})
メタ方法
Metamethods 是儲存在 metable 內的函數。它們可以從呼叫表到添加表,甚至分裂表。這是可用的 metamethodes 列表:
方法 | 說明 |
---|---|
__index(表, 索引) | 檢查時,如果表[index]是零,則會發生此事件。也可以將其設置為檢查表,在此檢查表會被索引。 |
__newindex(表、索引、值) | 當 table[index] 嘗試設定時 (table[index] = value),如果 table[index] 為空,也可以設為表,在此表將被索引。 |
__call(表、...) | 桌子被稱為「函數」時,發生這種情況是因為傳入的參數。 |
__concat(表, value) | 使用桌子上的.. 連接器操作時會發生火災。 |
__unm(表) | 使用 unary - 操作器時發射。 |
__add(表、值) | + 加算運算器。 |
__sub(表、值) | The - 子差運算器。 |
__mul(表、值) | 乘數操作器。 |
__div(表、值) | / 分裂器。 |
__idiv(表, value) | // 地板分裂器。 |
__mod(表、值) | % 模組運算器。 |
__pow(表、值) | ^ 乘數操作器。 |
__tostring(表) | 發射時,當 string 被呼叫在表上時。 |
__metable | 如果存在,則鎖定預設表,因此 getmetable 將會返回,而不是預設表,並且設置表 將會發生錯誤。非功能值。 |
__eq(表、值) | == 與操作器¹ 相等 |
__lt(表、值) | < 操作器¹ 以下 |
__le(表、值) | 操作符 "<" |
__模式 | 用於弱表,表示是否為弱表。 注意: 對於 Roblox 實例的引用永遠不會是弱的。 擁有此類引用的桌子將永遠不會被收集垃圾。 |
__gc(表) | 發生在桌子被收集垃圾時。 注意: 在 Roblox 中,此 metamethode 已停用。 |
__len(桌子) | 使用 # 長度操作器在對象上時發生。 |
__iter(表) | 用於指定範例化時使用的自訂執行程式。 |
您應該注意到,寫幾何或關係式數學方法的函數時,兩個功能參數之間是可以互相轉換的。 例如,當您使用向量操作來表示向量時,如果向量的分數不是乘數,則向量的向量量不是乘數。 因此,如果您正在為向量2 類的自己的向量寫代碼,您應該小心以帳算向量的任何場景。
local vector2 = {__type = "vector2"}
local mt = {__index = vector2}
function mt.__div(a, b)
if type(a) == "number" then
-- a 是一個向量,b 是一個向向量力
local scalar, vector = a, b
return vector2.new(scalar / vector.x, scalar / vector.y)
elseif type(b) == "number" then
-- a 是向向量力,b 是資料
local vector, scalar = a, b
return vector2.new(vector.x / scalar, vector.y / scalar)
elseif (a.__type and a.__type == "vector2" and b.__type and b.__type == "vector2") then
-- a 和 b 是向量
return vector2.new(a.x / b.x, a.y / b.y)
end
end
function mt.__tostring(t)
return t.x .. ", " .. t.y
end
function vector2.new(x, y)
local self = setmetatable({}, mt)
self.x = x or 0
self.y = y or 0
return self
end
local a = vector2.new(10, 5)
local b = vector2.new(-3, 4)
print(a / b) -- -3.333333333333, 1.25
print(b / a) -- 0.3、0.8
print(2 / a) -- 0.2、0.4
print(a / 2) -- 5, 2.5
使用メタ桁梱
有很多方法使用 metatables,例如 __unm metamethode (以使表為負):
local metatable = {
__unm = function(t) -- __unm 是 for the unary - 操作器
local negated = {}
for key, value in t do
negated[key] = -value -- 否定這個表中的所有值
end
return negated -- 返回表
end
}
local table1 = setmetatable({10, 11, 12}, metatable)
print(table.concat(-table1, "; ")) --> -10; -11; -12
這裡有一種使用 __index 來宣告事件的有趣方式:
local metatable = {__index = {x = 1}}local t = setmetatable({}, metatable)print(t.x) --> 1
__index 發生了當 x 在表中索引時並未找到,Lua 然後在 __index 桌子上搜尋到一個名為 x 的索引,然後,找到一個,然後返回。
現在您可以很容易地做到這一點,但從哪裡來的更多。以下是一些範例:
local t = {10, 20, 30}print(t(5))
現在,顯然你不能呼叫表。那是瘋了,但 (驚喜,驚喜!) 使用 metatables 你可以。
local metatable = {
__call = function(t, param)
local sum = {}
for i, value in ipairs(t) do
sum[i] = value + param -- 將參數 (5) 添加到值,然後放置在新表中 (t)。
end
return unpack(sum) -- 返回個別桌子值
end
}
local t = setmetatable({10, 20, 30}, metatable)
print(t(5)) --> 15 25 35
您也可以做更多,例如添加桌子!
local table1 = {10, 11, 12}local table2 = {13, 14, 15}for k, v in table1 + table2 doprint(k, v)end
這會發生錯誤,說你正試圖在桌子上執行算術。我們來試試這個使用 metatable。
local metatable = {
__add = function(t1, t2)
local sum = {}
for key, value in t1 do
sum[key] = value
end
for key, value in t2 do
if sum[key] then
sum[key] += value
else
sum[key] = value
end
end
return sum
end
}
local table1 = setmetatable({10, 11, 12}, metatable)
local table2 = setmetatable({13, 14, 15}, metatable)
for k, v in table1 + table2 do
print(k, v)
end
使用案例
現在,這些範例都可以作為簡單的函數實現,但你可以做得到更多。讓我們嘗試一個簡單的程序,它會記住一個數字,當可能發生延遲的數學問題放入它。
為此,我們將使用 __index 超級方法來簡化:
local function mathProblem(num)
for i = 1, 20 do
num = math.floor(num * 10 + 65)
end
for i = 1, 10 do
num += i - 1
end
return num
end
local metatable = {
__index = function(object, key)
local num = mathProblem(key)
object[key] = num
return num
end
}
local t = setmetatable({}, metatable)
print(t[1]) -- 因為這是第一次使用此數字,所以它必須執行數學函數。
print(t[2]) -- 會因為這是第一次使用此數字而感到慢。
print(t[1]) -- will be fast because it's just grabbing the number from the table.
原始,原始,原始等
當玩具表時,您可能會遇到一些問題。如果您需要使用 __index 的 metamethode 在表中創建新值,但该表的 metabel 也有一个 __newindex 的 metamethode 在它裡面? 您將想要使用 Lua 內置功能 rawset 來設置值,而不
local t = setmetatable({}, {
__index = function(self, i)
self[i] = i * 10 -- 只是為了說明
return self[i]
end,
__newindex = function(self, i, v)
--不要做任何事情,因為我們不想讓您將值設置在表中的正常方式
end
})
print(t[1]) -- Causes a C-Stack overflow
現在為什麼會發生這樣的錯誤? Stack overflows 發生在您嘗試從自己呼叫功能太多次時,但是什麼會造成這樣的發生? 在 __index 函數中,我們將 self[i] 設置為值,因此當它到達下一行時, <
問題是,__newindex 不讓我們設定值。它的存在會使值無法被添加到表中使用 standard t[i] = v 方法。為了解決此問題,您使用 rawset 函數。
local t = setmetatable({}, {
__index = function(self, i)
rawset(self, i, i * 10)
return self[i]
end,
__newindex = function(self, i, v)
--不要做任何事情,因為我們不想讓您將值設置在表中的正常方式
end
})
print(t[1]) -- prints 10
使用 Set 資料類型
A set 是一個沒有順序和重複元素的集合。一個項目或 是 或 不是 包含在套設定中。使用 metatables,您可以在 Lua 指令碼中構建和操作套組。
基本方法
下列代碼包括基本設定功能,讓您可以建立新集合、添加和移除一個項道具、檢查集合是否包含一個項道具,並輸出集合中的內容。
local Set = {}
Set.__index = Set
-- 功能從可選的項目列中建造一組
function Set.new(items)
local newSet = {}
for key, value in items or {} do
newSet[value] = true
end
return setmetatable(newSet, Set)
end
-- 功能可以將一個項目添加到一個設定合
function Set:add(item)
self[item] = true
end
-- 功能可以從一設定中移除一項物品
function Set:remove(item)
self[item] = nil
end
-- 檢查設置是否包含物道具的功能
function Set:contains(item)
return self[item] == true
end
-- 功能要輸出集合為檢查錯誤的 comma 分號列表
function Set:output()
local elems = {}
for key, value in self do
table.insert(elems, tostring(key))
end
print(table.concat(elems, ", "))
end
創建套件
您可以使用 Set.new() 與可選擇的一個資料集合來建立新集合。
local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})
注意,根據定義,集合沒有排序的概念。
添加物品
將一個項目添加到現有集合中可以使用 Set:add() 方法。
local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})fruits:add("Mango")
移除項目
要從設定合中移除項目,請使用 Set:remove() 與項目名稱。
local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})fruits:remove("Orange")
檢查項目
要檢查設置是否包含特定項道具,請使用 Set:contains()。
local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})local result1 = fruits:contains("Cherry")print(result1) -- 真的local result2 = fruits:contains("Watermelon")print(result2) -- false
額外方法
其他有用的操作可以為集團實現,讓您可以比較項目之間的項目、結合集團或從另一個集團減去一個集團。
交叉
當考慮集合為 Venn 圖形時,您可以以下方式獲得兩個集合的 交叉點,即意味著在兩個集合中出現的項目。
local function getIntersection(set1, set2)
local result = Set.new()
for key, value in set1 do
if set2:contains(key) then
result:add(key)
end
end
return result
end
local freshFruits = Set.new({"Mango", "Lemon", "Orange", "Cherry", "Lime", "Peach"})
local frozenFruits = Set.new({"Mango", "Peach", "Pineapple"})
local commonFruits = getIntersection(freshFruits, frozenFruits)
commonFruits:output() -- Mango, Peach
聯盟
您可以使用以下功能來取得兩個集合的兩個元素,這意味著在兩個集合中的項目的集合,沒有重複。注意,此方法使用 __add 方法提供一個額外的快捷方式 __add。
function Set:__add(otherSet)
local result = Set.new()
for entry in self do
result[entry] = true
end
for entry in otherSet do
result[entry] = true
end
return result
end
local sweetFruits = Set.new({"Apple", "Mango", "Cherry", "Peach"})
local sourFruits = Set.new({"Lemon", "Lime"})
local allFruits = sweetFruits + sourFruits
allFruits:output() -- 桃子、柳橙、蘋果、櫻桃、檸檬、芒果
減法
您可以使用以下功能從另一個集合中移除所有項目到另一個集合中的項目。與上述功能相似,這使用 metatable __sub 方法提供一個子算術快捷方式 set1 - set2。
function Set:__sub(otherSet)
local result = Set.new()
for entry in self do
result[entry] = true
end
for entry in otherSet do
result[entry] = nil
end
return result
end
local allFruits = Set.new({"Apple", "Lemon", "Mango", "Cherry", "Lime", "Peach"})
local sourFruits = Set.new({"Lemon", "Lime"})
local sweetFruits = allFruits - sourFruits
sweetFruits:output() -- Mango, Apple, Cherry, Peach