虚拟桌子可以让桌子变得比以前更强大。它们与数据附加在一起,包含名为 metamethodes 的值。 metamethodes 是使用数据附加在它上的特定操作时触发的。
考虑以下验证码:
local list = {1, 2}print(list[3])
您可能期望此代码在列表中搜索第三个索引,找到无结果,然后返回零。 但这并不正确。 实际上,代码在列表中搜索第三个索引,找到无结果,然后检查是否有附加的 metatable 对表,如果没有,就会返回 nil。
操作元数据
添加和找到桌子的 mettab 的两个主要功能是 setmetatable 和 getmetatable
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 doprint(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