算术运算的元方法 Metamethods

x33g5p2x  于2022-08-17 转载在 其他  
字(1.5k)|赞(0)|评价(0)|浏览(523)

这一部分我们通过一个简单的例子介绍如何使用 metamethods。假定我们使用 table来描述结合,使用函数来描述集合的并操作,交集操作, like 操作。我们在一个表内定义这些函数,然后使用构造函数创建一个集合:

为了帮助理解程序运行结果,我们也定义了打印函数输出结果:

现在我们想加号运算符(+)执行两个集合的并操作,我们将所有集合共享一个metatable,并且为这个 metatable 添加如何处理相加操作。

第一步,我们定义一个普通的表,用来作为 metatable。为避免污染命名空间,我们将其放在 set 内部。

Set.mt = {} -- metatable for sets

第二步,修改 set.new 函数,增加一行,创建表的时候同时指定对应的 metatable

function Set.new (t) -- 2nd version
local set = {}
setmetatable(set, Set.mt)
for _, l in ipairs(t) do set[l] = true end
return set
end

这样一来, set.new 创建的所有的集合都有相同的 metatable 了:

下图可知s1和s2的元表相同。

第三步,给 metatable 增加__add 函数。

Lua 试图对两个集合相加时,将调用这个函数,以两个相加的表作为参数。通过 metamethod,我们可以对两个集合进行相加:

同样的我们可以使用相乘运算符来定义集合的交集操作:

结果如下:

对于每一个算术运算符, metatable 都有对应的域名与其对应,除了__add、 __mul外,还有__sub(减)、 __div(除)、 __unm(负)、 __pow(幂),我们也可以定义__concat 定义连接行为。

当我们对两个表进行加没有问题,但如果两个操作数有不同的 metatable 例如:

Lua 选择 metamethod 的原则:如果第一个参数存在带有__add 域的 metatableLua使用它作为 metamethod,和第二个参数无关;

否则第二个参数存在带有__add 域的 metatableLua 使用它作为 metamethod 否则报错。

Lua 不关心这种混合类型的,如果我们运行上面的 s=s+8 的例子在 Set.union 发生错误:

C:\Users\Administrator\.vscode\extensions\actboy168.lua-debug-1.59.0\runtime\win32-x64\lua54\lua.exe: ...ments\Tencent Files\1978045947\FileRecv\Lua代码/main.lua:2593: attempt to 'add' a set with a non-set value
stack traceback:
        [C]: in function 'error'
        ...ments\Tencent Files\1978045947\FileRecv\Lua代码/main.lua:2542: in metamethod 'add'
        ...ments\Tencent Files\1978045947\FileRecv\Lua代码/main.lua:2593: in main chunk
        [C]: in ?

如果我们想得到更加清楚地错误信息,我们需要自己显式的检查操作数的类型:

function Set.union (a,b)
if getmetatable(a) ~= Set.mt or
getmetatable(b) ~= Set.mt then
error("attempt to `add' a set with a non-set value", 2)
end
... -- same as before

相关文章