元表格

*此內容是使用 AI(Beta 測試版)翻譯,可能含有錯誤。若要以英文檢視此頁面,請按一下這裡

元表允許表變得比以前更強大。它們附於資料並包含稱為 metamethods 的值。當特定動作使用與其附帶的數據一起時,會發射元方法。

操作元數桌

添加和找到表的可匹配功能的兩個主要功能是 setmetatable()getmetatable()


local x = {}
local metaTable = {} -- 元表也是表!
setmetatable(x, metaTable) -- 給 x 一個叫做 metaTable 的可轉換表!
print(getmetatable(x)) --> table: [hexadecimal memory address]

setmetatable() 功能也返回你設置 metatable 的表,因此這兩個腳本做相同的事情:


local x = {}
setmetatable(x, {})

local x = setmetatable({}, {})

元方法

元方法是儲存在元表中的功能。他們可以從呼叫表開始,到添加表,甚至分割表。以下是可用的元方法列表:

方法說明
__index(table, index)table[index] 被索引時,如果 table[index]nil,也會發生火災。也可以設為表,在此情況下,該表將被索引。
__newindex(table, index, value)table[index] 嘗試設置 (table[index] = value) 時,發生火災,如果 table[index]nil 。也可以設為表,在此情況下,該表將被索引。
__call(table, ...)當表稱為函數時發生火災, ... 是傳送的參數。
__concat(table, value)當在表上使用 .. 連接操作符時,發生火災。
__unm(table)當在表上使用單元操作 時發生火災。
__add(table, value)+ 添加操作符。
__sub(table, value) 減法運算器。
__mul(table, value)* 乘法器。
__div(table, value)/ 分割操作器。
__idiv(table, value)//樓層分割操作器。
__mod(table, value)%模組運算器。
__pow(table, value)^ 乘法子操作器。
__tostring(table)當表上呼叫 tostring 時發射。
__metatable如果存在,將metatable鎖定,因此 getmetatable() 將返回此而不是metatable,而 setmetatable() 將發生錯誤。非功能值。
__eq(table, value)等於操作器¹的 ==
__lt(table, value)小於操作器¹的<
__le(table, value)<= 運作者¹
__mode在虛弱的表中使用,宣言表的鑰匙和/或值是否虛弱。請注意,引用 Roblox 實例永遠不會虛弱。擁有此類參考的表永遠不會被回收垃圾。
__len(table)當在對物件上使用 # 長度運算符時發生火災。
__iter(table)用於指示在使用一般化循環時使用自訂迭代器。

應該注意的是,當寫入算術或關聯超級方法的功能時,兩個功能參數可以在發射超級方法的表和其他值之間交換。例如,當使用向量操作時,分數分配不是 commutative。因此,如果你寫了自己的 vector2 類別的 metamethods,你想要小心地考慮兩種情況。


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 是 scalar
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.3333333333333, 1.25
print(b / a) -- -0.3, 0.8
print(2 / a) -- 0.2, 0.4
print(a / 2) -- 5, 2.5

使用

有許多方法可以使用元表,例如 __unm 元方法使表變為負:


local metatable = {
__unm = function(t) -- __unm 是單元操作符的一部分
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 未找到時,被發射了。Luau 然後搜尋了 __index 表中一個名為 x 的索引,找到一個後返回了。

現在您可以使用簡單的函數輕鬆做到這一點,但那從哪裡來的還有很多。舉例來說:


local t = {10, 20, 30}
print(t(5))

通常您無法呼叫表,但使用元表時您可以:


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 do
print(k, v)
end

這將錯誤地說明您正在嘗試對表進行算術運算,但使用可轉換表時會正常運作:


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 元方法來在表中創建新值,但該表的元表也有一個 __newindex 元方法,你想使用 Luau 內置函數 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 stack overflow

堆棧過滿發生時你嘗試從自己太多次呼叫函數時。在上面的 __index 功能中, self[i] 設為值,因此當它到達下一行時,self[i] 應該存在,並且可能不會呼叫 __index 的 metamethod。問題是 __newindex 不讓你設置值。它的存在會停止值被添加到表中的標準 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]) --> 10

使用設定的數據類型

一個 集合 是一個沒有順序且沒有重複元素的集合。一個項目是 或 不包含在設定合中 。使用元表,您可以在 Luau 腳本中構建和操作集。

基本方法

下列代碼包含基本集功能,讓您構建新集、添加和移除項道具、檢查集是否包含項道具,並輸出設定的內容。


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
-- 功能以輸出設為分隔符分列的調試列表
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

額外方法

其他有用的操作可以對集合進行實現,讓您比較集合之間的項目、結合集合或從一個集合中減去一個集合。

交叉

當考慮集合作為維納圖時,您可以將兩個集合的 交集 獲得如下,這意味著出現在 兩個集合 中的項目。


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

聯盟

您可以使用以下函數獲得兩個集合的 聯盟 ,即兩個集合中的項目集沒有重複。請注意,此功能使用 metatable __add 方法提供一個額外的簡化路徑 set1 + set2


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() -- 桃子、檸檬、蘋果、櫻桃、檢查、芒果

減法

您可以透過以下功能將一組中的所有項目從另一組中移除到另一組中。與上面的功能類似,這裡使用可轉換的 __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