有没有办法填充 Lua 5.1 以支持比较元方法?
Is there a way to shim Lua 5.1 to support comparison meta methods?
Lua 的大多数版本都支持 table 上的算术比较运算符。例如考虑这个场景,简化为处理各种单位长度的抽象:
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
,因为元 table 有一个 __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()
。
考虑到 volume and complexity of code involved,如果我可以填充 Lua 5.1 来支持它,那就太好了。有什么办法可以说服它允许将 table 与数字进行比较?
这恐怕是不可能的。如果您比较 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
函数时,由于在代码中发现 <
而发出该函数,这些实现会做截然不同的事情。当旧的解释器看到不同类型的操作数时,它会很快退出。因此,根本无法将未包装的数字与 table.
进行比较
Lua 的大多数版本都支持 table 上的算术比较运算符。例如考虑这个场景,简化为处理各种单位长度的抽象:
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
,因为元 table 有一个 __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()
。
考虑到 volume and complexity of code involved,如果我可以填充 Lua 5.1 来支持它,那就太好了。有什么办法可以说服它允许将 table 与数字进行比较?
这恐怕是不可能的。如果您比较 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
函数时,由于在代码中发现 <
而发出该函数,这些实现会做截然不同的事情。当旧的解释器看到不同类型的操作数时,它会很快退出。因此,根本无法将未包装的数字与 table.