如何使Lua 5.1支持比较元方法的插接?

大多数Lua版本都支持对表进行算术比较运算符的操作。例如,考虑以下场景,将不同的单位长度抽象为一个问题:

local function convert_units (input)
    if type(input) ~= "table" then
        return input
    else
        if input.unit == "cm" then
            return input.amount * 10
        else
            return input.amount
        end
    end
end

local mt = {
    __lt = function (self, other)
        return convert_units(self) < convert_units(other)
    end
}

local a = {
    amount = 1.2,
    unit = "cm"
}

local b = {
    amount = 14,
    unit = "mm"
}

setmetatable(a, mt)
setmetatable(b, mt)

print(a < b)

这将输出"true",因为元表具有'__lt'的方法,将对象强制转换为兼容的单位,然后对它们进行比较。这个代码可以在Lua 5.1,5.2和5.3中运行。

当您尝试执行不同类型的操作时,问题就出现了:

print (a < 13)

这将在Lua 5.2和5.3中工作,但在Lua 5.1中,它会抛出错误:

lua5.1: attempt to compare number with table

数学元方法完全能够处理比较中一侧的原始数字,但Lua 5.1甚至不会尝试。

遗憾的是,我需要能够支持多种Lua解释器。考虑到最低公共分母是Lua 5.1,这将意味着始终使原始数字具有类似的对象实例化或始终在编写比较时使用'convert_units()'。

鉴于[涉及的代码量和复杂性](https://github.com/sile -typesetter/sile/pull/751),如果我能够使Lua 5.1支持这一点,那将非常不错。有没有办法说服它允许将表与数字进行比较呢?

点赞
用户752976
用户752976

我很抱歉,这是不可能的。如果你比较一下 5.1 和 5.3 版本的 luaV_lessthan 实现,可能会有所帮助:

int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r) {
  int res;
  if (ttype(l) != ttype(r))
    return luaG_ordererror(L, l, r);
  else if (ttisnumber(l))
    return luai_numlt(nvalue(l), nvalue(r));
  else if (ttisstring(l))
    return l_strcmp(rawtsvalue(l), rawtsvalue(r)) < 0;
  else if ((res = call_orderTM(L, l, r, TM_LT)) != -1)
    return res;
  return luaG_ordererror(L, l, r);
}

5.3 版本:

int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r) {
  int res;
  if (ttisnumber(l) && ttisnumber(r))  /* both operands are numbers? */
    return LTnum(l, r);
  else if (ttisstring(l) && ttisstring(r))  /* both are strings? */
    return l_strcmp(tsvalue(l), tsvalue(r)) < 0;
  else if ((res = luaT_callorderTM(L, l, r, TM_LT)) < 0)  /* no metamethod? */
    luaG_ordererror(L, l, r);  /* error */
  return res;
}

如你所见,当执行 lessthan 函数时,如果在代码中发现<,这些实现会进行完全不同的操作。当旧解释器看到不同类型的操作数时,它会非常快地退出。所以,没有办法让裸数字与表进行比较。

2020-01-09 15:45:00