Lua __eq 在具有不同元表的表上

Lua __eq on tables with different metatables

我在本网站 http://lua-users.org/wiki/MetamethodsTutorial 上找到了以下引述:

__eq is called when the == operator is used on two tables, the reference equality check failed, and both tables have the same __eq metamethod (!).

现在我用 Lua 5.3.5 测试了它,这根本不是我观察到的:

a = {}
b = {}
m = {}
m2 = {}
setmetatable(a, m)
setmetatable(b, m2)
m.__eq = function(p1, p2) print("why"); return true end
m2.__eq = function(p1, p2) print("why2"); return true end

这是我测试过的代码。

> a == b
why
true
> b == a
why2
true

它看起来与比较运算符做同样的事情,只是取左边的 table 并使用它的元方法。

这是否在最近的 Lua 版本中发生了变化,还是我的测试有误?

感谢您的帮助。

在 Lua 5.3 中发生了变化。 The readme says it introduced "more flexible rules for some metamethods". Compare the Lua 5.2 reference manual:

the == operation. The function getequalhandler defines how Lua chooses a metamethod for equality. A metamethod is selected only when both values being compared have the same type and the same metamethod for the selected operation, and the values are either tables or full userdata.

     function getequalhandler (op1, op2)
       if type(op1) ~= type(op2) or
          (type(op1) ~= "table" and type(op1) ~= "userdata") then
         return nil     -- different values
       end
       local mm1 = metatable(op1).__eq
       local mm2 = metatable(op2).__eq
       if mm1 == mm2 then return mm1 else return nil end
     end

The "eq" event is defined as follows:

     function eq_event (op1, op2)
       if op1 == op2 then   -- primitive equal?
         return true   -- values are equal
       end
       -- try metamethod
       local h = getequalhandler(op1, op2)
       if h then
         return not not h(op1, op2)
       else
         return false
       end
     end

Note that the result is always a boolean.

the Lua 5.3 reference manual:

the equal (==) operation. Behavior similar to the addition operation, except that Lua will try a metamethod only when the values being compared are either both tables or both full userdata and they are not primitively equal. The result of the call is always converted to a boolean.