元表还允许我们指定关系运算符的含义,其中的元方法包括等于( __eq
)、小于( __lt
)、和小于等于( __le
)。其他三个关系运算符没有单独的元方法, Lua
语言会将 a ~= b
转换为 not (a == b)
, a > b
转换为 b < a
, a >= b
转换为 b <= a
。
在 Lua
语言的老版本中, Lua
语言会通过将 a <= b
转换为 not (b < a)
来把所有的关系运算符转化为一个关系运算符。不过,这种转换在遇到部分有序时就会不正确。所谓部分有序是指,并非所有类型的元素都能够被正确的排序。例如,由于 Not a Number
( NaN
)的存在,大多数计算机中的浮点数就不是完全可以排序的。根据 IEEE754
标准, NaN
代表未定义的值,例如 0/0
的结果就是 NaN
。标准规定任何涉及 NaN
的比较都应返回假,这就意味着 NaN <= x
永远为假, x <= NaN
也为假。因此,在这种情况下, a <= b
到 not (b < a)
的转化也就不合法了。
在集合的实例中,我们也面临类似的问题。 <=
显而易见且有用的含义是集合包含: a <= b
通常意味着 a
是 b
的一个子集。然而,根据部分有序的定义, a <= b
和 b < a
可能同时为假。因此,我们就必须实现 __le
(小于等于,子集关系)和 __lt
(小于,真子集关系):
mt.__le = function (a, b) for k in pairs(a) do if not b[k] then return false end end return true end mt.__lt = function (a, b) return a <= b and not (b <= a) end
最后,我们还可以通过集合包含来定义集合相等:
mt.__eq = function (a, b) return a <= b and b <= a end
有了这些定义后,我们就可以比较集合了:
s1 = Set.new{2, 4} s2 = Set.new{4, 10, 2} print(s1 <= s2) --> true print(s1 < s2) --> true print(s1 >= s1) --> true print(s1 > s1) --> false print(s1 == s2 * s1) --> true
注意
相等比较有一些限制。如果两个对象的类型不同,那么相等比较操作不会调用任何元方法而直接返回 false
。因此,不过元方法如何,集合永远不等于数字。