元表

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

元表使表格比以前更強大。它們附加到數據上,並包含稱為元方法的值。當對附加的數據執行某種操作時,元方法會被觸發。

操作元表

添加和查找表的元表的兩個主要功能是 setmetatable()getmetatable()


local x = {}
local metaTable = {} -- 元表也是表格!
setmetatable(x, metaTable) -- 為 x 設置一個名為 metaTable 的元表!
print(getmetatable(x)) --> table: [十六進制內存地址]

setmetatable() 函數還會返回設置元表的表,因此這兩個腳本執行的操作是相同的:


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如果存在,鎖定元表,以便 getmetatable() 將返回此值而不是元表,並且 setmetatable() 將引發錯誤。非函數值。
__eq(table, value)== 等於運算符¹
__lt(table, value)< 小於運算符¹
__le(table, value)<= 運算符¹
__mode用於弱表,聲明表的鍵和/或值是否為弱。請注意,對 Roblox 實例的引用永遠不會是弱的。持有此類引用的表永遠不會被垃圾回收。
__len(table)當對該對象使用 # 長度運算符時觸發。
__iter(table)用於表示自定義迭代器,當使用泛型迭代時。

需要注意的是,在為算術或關係元方法編寫函數時,兩個函數參數在觸發元方法的表和另一個值之間是可互換的。例如,在進行標量的向量操作時,除法不是交換的。因此,如果您為自己的 vector2 類編寫元方法,您會希望小心考慮這兩種情況。


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.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

x 在表中被索引且未找到時,__index 被觸發。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]) -- 導致棧溢出

棧溢出發生在您嘗試從自身調用函數太多次的時候。在上面的 __index 函數中,self[i] 被設置為一個值,因此當進入下一行時,self[i] 應該存在,並且應該不會調用 __index 元方法。問題在於 __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) -- true
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

聯集

您可以通過以下函數獲得兩個集合的聯集,即包含兩個集合中的項目且沒有重複的集合。注意,這個函數使用了元表的 __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() -- Peach, Lime, Apple, Cherry, Lemon, Mango

減法

您可以通過以下函數從一個集合中移除另一個集合中的所有項目。類似於上面的函數,這個函數使用元表的 __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
©2026 Roblox Corporation、Roblox、Roblox 標誌及 Powering Imagination 是我們在美國及其他國家地區的部分註冊與未註冊商標。