金属表

*此内容使用人工智能(Beta)翻译,可能包含错误。若要查看英文页面,请点按 此处

虚拟桌子可以让桌子变得比以前更强大。它们与数据附加在一起,包含名为 metamethodes 的值。 metamethodes 是使用数据附加在它上的特定操作时触发的。

考虑以下验证码:


local list = {1, 2}
print(list[3])

您可能期望此代码在列表中搜索第三个索引,找到无结果,然后返回零。 但这并不正确。 实际上,代码在列表中搜索第三个索引,找到无结果,然后检查是否有附加的 metatable 对表,如果没有,就会返回 nil。

操作元数据

添加和找到桌子的 mettab 的两个主要功能是 setmetatablegetmetatable


local x = {}
local metaTable = {} -- MetaTables 也是表!
setmetatable(x, metaTable) -- 给 x 一个名为 metaTable 的 metatable!
print(getmetatable(x)) --> table: [hexadecimal memory address]

setmetatable 函数还返回您设置 metatable 的表,因此这两个脚本都做到了同一点:


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

local x = setmetatable({}, {})

メタ方法

Metamethodes 是存储在 metatable 中的函数。它们可以从调用表到添加表,甚至还可以将表分为多个。以下是可用的 metamethodes 列表:

方法描述
__index(表,索引)索引表[index]时,表[index]为空。也可以设为表,在这种情况下,该表将被索引。
__新索引(表、索引、值)当 table[index] 尝试设置时,如果 table[index] 为空,表[index] 将被设置。可以将 table 设置为表,在这种情况下,该表将被索引。
__call(表,...)当表被调用为函数时,... 是被传递的参数。
__concat(表、值)使用 .. 连接器在桌子上时,表达式将会发生。
__unm(表)使用 unary - 操作器时会发生。
__add(表,值)+ 操作器。
__sub(表,值)– 减法操作器。
__mul(表,值)* 倍增操作器。
__div(表,值)/ 分割操作器。
__idiv(表,值)// 楼层分割器。
__mod(表,值)% modulus 操作器。
__pow(表,值)^ 倍数操作器。
__tostring(表)在表上调用时,表示符串。
__桌子如果存在,锁定 metatable ,以便 getmetatable 返回这个而不是 metatable 和 setmetablo 会错误。 非功能性值。
__eq(表、值)== 等于操作符¹
__lt(表,值)< 不是操作员¹
__le(表、值)The operator¹
__模式用于弱表,用于表中键和/或值是否为弱。 注意: 参考到 Roblox 实例的引用永远不会是弱的。 表中存储此参考的表不会被收集垃圾。
__gc(桌子)发生在表达式收集到垃圾时。 注意: 在 Roblox 上,此 metamethod 已禁用。
__len(表)使用 # 长度操作器在对象上时发生。
__iter(表)用于指定使用通用循环时使用的自定义继承器。

您应该注意,当为 arithmetic 或关联式 metameth 函数写代码时,两个参数参数之间是可交换的。例如,当使用向量操作时,如果向量分数不是命令,则可能需要进行向量乘法。因此,如果您为自己的向量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
-- both 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

有许多使用 metatables 的方法,例如 __unm 的 metamethode (使用表达式来使表为负):


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 在表中索引时不找到时。 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 do
print(k, v)
end

这将在说明你正试图在表上执行 arithmetic 时出错。让我们使用 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

现在为什么会导致叠加过载?叠加过载发生当您尝试从自己调用函数太多次时,但是什么会导致它发生?在 __index 函数中,我们将 self[i] 设置为值,因此当它到达下一行时, self[i] 就会存在

问题是,__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]) -- prints 10

使用 Set 数据类型

一个 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
-- 输出设置为时间为空格列表以便调试
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