Lua 中的逐个元素比较
Element by Element comparison in Lua
我正在尝试找到一种使用标准 <
运算符在 Lua 中进行逐个元素比较的方法。例如,这是我想做的:
a = {5, 7, 10}
b = {6, 4, 15}
c = a < b -- should return {true, false, true}
我已经有了用于加法(和减法、乘法等)的代码。我的问题是 Lua 强制将比较结果与布尔值进行比较。我不想要一个布尔值,我想要一个 table 作为比较的结果。
到目前为止,这是我的代码,加法有效,但小于比较无效:
m = {}
m['__add'] = function (a, b)
-- Add two tables together
-- Works fine
c = {}
for i = 1, #a do
c[i] = a[i] + b[i]
end
return c
end
m['__lt'] = function (a, b)
-- Should do a less-than operator on each element
-- Doesn't work, Lua forces result to boolean
c = {}
for i = 1, #a do
c[i] = a[i] < b[i]
end
return c
end
a = {5, 7, 10}
b = {6, 4, 15}
setmetatable(a, m)
c = a + b -- Expecting {11, 11, 25}
print(c[1], c[2], c[3]) -- Works great!
c = a < b -- Expecting {true, false, true}
print(c[1], c[2], c[3]) -- Error, lua makes c into boolean
Lua 编程手册说 __lt
元方法调用的结果总是转换为布尔值。我的问题是,我该如何解决这个问题?我听说 Lua 对 DSL 很有用,我真的需要语法才能在这里工作。我认为使用 MetaLua 应该是可能的,但我不确定从哪里开始。
一位同事建议我只使用 <<
而不是 __shl
元方法。我尝试了它并且它有效,但我真的想使用 <
来表示小于,而不是使用错误符号的 hack。
谢谢!
比较 Lua return 一个布尔值。
除了更改 Lua 的核心之外,您无能为力。
正如其他人已经提到的,对此没有直接的解决方案。但是,通过使用通用的 Python-like zip() 函数,如下所示,您可以简化问题,如下所示:
--------------------------------------------------------------------------------
-- Python-like zip() iterator
--------------------------------------------------------------------------------
function zip(...)
local arrays, ans = {...}, {}
local index = 0
return
function()
index = index + 1
for i,t in ipairs(arrays) do
if type(t) == 'function' then ans[i] = t() else ans[i] = t[index] end
if ans[i] == nil then return end
end
return table.unpack(ans)
end
end
--------------------------------------------------------------------------------
a = {5, 7, 10}
b = {6, 4, 15}
c = {}
for a,b in zip(a,b) do
c[#c+1] = a < b -- should return {true, false, true}
end
-- display answer
for _,v in ipairs(c) do print(v) end
你能忍受有点冗长的 v()
-notation:
v(a < b)
而不是 a < b
?
local vec_mt = {}
local operations = {
copy = function (a, b) return a end,
lt = function (a, b) return a < b end,
add = function (a, b) return a + b end,
tostring = tostring,
}
local function create_vector_instance(operand1, operation, operand2)
local func, vec = operations[operation], {}
for k, elem1 in ipairs(operand1) do
local elem2 = operand2 and operand2[k]
vec[k] = func(elem1, elem2)
end
return setmetatable(vec, vec_mt)
end
local saved_result
function v(...) -- constructor for class "vector"
local result = ...
local tp = type(result)
if tp == 'boolean' and saved_result then
result, saved_result = saved_result
elseif tp ~= 'table' then
result = create_vector_instance({...}, 'copy')
end
return result
end
function vec_mt.__add(v1, v2)
return create_vector_instance(v1, 'add', v2)
end
function vec_mt.__lt(v1, v2)
saved_result = create_vector_instance(v1, 'lt', v2)
end
function vec_mt.__tostring(vec)
return
'Vector ('
..table.concat(create_vector_instance(vec, 'tostring'), ', ')
..')'
end
用法:
a = v(5, 7, 10); print(a)
b = v(6, 4, 15); print(b)
c = a + b ; print(c) -- result is v(11, 11, 25)
c = v(a + b); print(c) -- result is v(11, 11, 25)
c = v(a < b); print(c) -- result is v(true, false, true)
您只有两种选择可以使它与您的语法一起工作:
选项 1:修补 Lua 核心。
这可能会非常困难,将来会成为维护的噩梦。最大的问题是 Lua 假设比较运算符 <
、>
、==
、~=
return 是一个布尔值价值。
Lua 生成的字节码实际上会在任何比较中跳转。例如,像 c = 4 < 5
这样的东西被编译成看起来更像 if (4 < 5) then c = true else c = false end
.
的字节码
您可以通过 luac -l file.lua
查看字节码的样子。如果将 c=4<5
的字节码与 c=4+5
的字节码进行比较,您就会明白我的意思。添加代码更短更简单。 Lua 假定您将通过比较而不是赋值进行分支。
选项 2:解析您的代码,更改它,然后运行
这是我认为你应该做的。这将非常困难,希望大部分工作已经为您完成(使用 LuaMinify 之类的东西)。
首先,编写一个可用于比较任何事物的函数。这里的想法是,如果它是 table,则进行特殊比较,但对其他一切使用 <
。
my_less = function(a, b)
if (type(a) == 'table') then
c = {}
for i = 1, #a do
c[i] = a[i] < b[i]
end
return c
else
return a < b
end
end
现在我们需要做的就是用 my_less(a,b)
.
替换每个小于运算符 a<b
让我们使用 LuaMinify 中的解析器。我们将使用以下代码调用它:
local parse = require('ParseLua').ParseLua
local ident = require('FormatIdentity')
local code = "c=a*b<c+d"
local ret, ast = parse(code)
local _, f = ident(ast)
print(f)
所有这一切都将把代码解析成语法树,然后再吐出来。我们将更改 FormatIdentity.lua
以使其进行替换。将第 138 行附近的部分替换为以下代码:
elseif expr.AstType == 'BinopExpr' then --line 138
if (expr.Op == '<') then
tok_it = tok_it + 1
out:appendStr('my_less(')
formatExpr(expr.Lhs)
out:appendStr(',')
formatExpr(expr.Rhs)
out:appendStr(')')
else
formatExpr(expr.Lhs)
appendStr( expr.Op )
formatExpr(expr.Rhs)
end
仅此而已。它会将 c=a*b<c+d
替换为 my_less(a*b,c+d)
。只需在 运行 时间推送所有代码即可。
我正在尝试找到一种使用标准 <
运算符在 Lua 中进行逐个元素比较的方法。例如,这是我想做的:
a = {5, 7, 10}
b = {6, 4, 15}
c = a < b -- should return {true, false, true}
我已经有了用于加法(和减法、乘法等)的代码。我的问题是 Lua 强制将比较结果与布尔值进行比较。我不想要一个布尔值,我想要一个 table 作为比较的结果。
到目前为止,这是我的代码,加法有效,但小于比较无效:
m = {}
m['__add'] = function (a, b)
-- Add two tables together
-- Works fine
c = {}
for i = 1, #a do
c[i] = a[i] + b[i]
end
return c
end
m['__lt'] = function (a, b)
-- Should do a less-than operator on each element
-- Doesn't work, Lua forces result to boolean
c = {}
for i = 1, #a do
c[i] = a[i] < b[i]
end
return c
end
a = {5, 7, 10}
b = {6, 4, 15}
setmetatable(a, m)
c = a + b -- Expecting {11, 11, 25}
print(c[1], c[2], c[3]) -- Works great!
c = a < b -- Expecting {true, false, true}
print(c[1], c[2], c[3]) -- Error, lua makes c into boolean
Lua 编程手册说 __lt
元方法调用的结果总是转换为布尔值。我的问题是,我该如何解决这个问题?我听说 Lua 对 DSL 很有用,我真的需要语法才能在这里工作。我认为使用 MetaLua 应该是可能的,但我不确定从哪里开始。
一位同事建议我只使用 <<
而不是 __shl
元方法。我尝试了它并且它有效,但我真的想使用 <
来表示小于,而不是使用错误符号的 hack。
谢谢!
比较 Lua return 一个布尔值。
除了更改 Lua 的核心之外,您无能为力。
正如其他人已经提到的,对此没有直接的解决方案。但是,通过使用通用的 Python-like zip() 函数,如下所示,您可以简化问题,如下所示:
--------------------------------------------------------------------------------
-- Python-like zip() iterator
--------------------------------------------------------------------------------
function zip(...)
local arrays, ans = {...}, {}
local index = 0
return
function()
index = index + 1
for i,t in ipairs(arrays) do
if type(t) == 'function' then ans[i] = t() else ans[i] = t[index] end
if ans[i] == nil then return end
end
return table.unpack(ans)
end
end
--------------------------------------------------------------------------------
a = {5, 7, 10}
b = {6, 4, 15}
c = {}
for a,b in zip(a,b) do
c[#c+1] = a < b -- should return {true, false, true}
end
-- display answer
for _,v in ipairs(c) do print(v) end
你能忍受有点冗长的 v()
-notation:
v(a < b)
而不是 a < b
?
local vec_mt = {}
local operations = {
copy = function (a, b) return a end,
lt = function (a, b) return a < b end,
add = function (a, b) return a + b end,
tostring = tostring,
}
local function create_vector_instance(operand1, operation, operand2)
local func, vec = operations[operation], {}
for k, elem1 in ipairs(operand1) do
local elem2 = operand2 and operand2[k]
vec[k] = func(elem1, elem2)
end
return setmetatable(vec, vec_mt)
end
local saved_result
function v(...) -- constructor for class "vector"
local result = ...
local tp = type(result)
if tp == 'boolean' and saved_result then
result, saved_result = saved_result
elseif tp ~= 'table' then
result = create_vector_instance({...}, 'copy')
end
return result
end
function vec_mt.__add(v1, v2)
return create_vector_instance(v1, 'add', v2)
end
function vec_mt.__lt(v1, v2)
saved_result = create_vector_instance(v1, 'lt', v2)
end
function vec_mt.__tostring(vec)
return
'Vector ('
..table.concat(create_vector_instance(vec, 'tostring'), ', ')
..')'
end
用法:
a = v(5, 7, 10); print(a)
b = v(6, 4, 15); print(b)
c = a + b ; print(c) -- result is v(11, 11, 25)
c = v(a + b); print(c) -- result is v(11, 11, 25)
c = v(a < b); print(c) -- result is v(true, false, true)
您只有两种选择可以使它与您的语法一起工作:
选项 1:修补 Lua 核心。
这可能会非常困难,将来会成为维护的噩梦。最大的问题是 Lua 假设比较运算符 <
、>
、==
、~=
return 是一个布尔值价值。
Lua 生成的字节码实际上会在任何比较中跳转。例如,像 c = 4 < 5
这样的东西被编译成看起来更像 if (4 < 5) then c = true else c = false end
.
您可以通过 luac -l file.lua
查看字节码的样子。如果将 c=4<5
的字节码与 c=4+5
的字节码进行比较,您就会明白我的意思。添加代码更短更简单。 Lua 假定您将通过比较而不是赋值进行分支。
选项 2:解析您的代码,更改它,然后运行
这是我认为你应该做的。这将非常困难,希望大部分工作已经为您完成(使用 LuaMinify 之类的东西)。
首先,编写一个可用于比较任何事物的函数。这里的想法是,如果它是 table,则进行特殊比较,但对其他一切使用 <
。
my_less = function(a, b)
if (type(a) == 'table') then
c = {}
for i = 1, #a do
c[i] = a[i] < b[i]
end
return c
else
return a < b
end
end
现在我们需要做的就是用 my_less(a,b)
.
a<b
让我们使用 LuaMinify 中的解析器。我们将使用以下代码调用它:
local parse = require('ParseLua').ParseLua
local ident = require('FormatIdentity')
local code = "c=a*b<c+d"
local ret, ast = parse(code)
local _, f = ident(ast)
print(f)
所有这一切都将把代码解析成语法树,然后再吐出来。我们将更改 FormatIdentity.lua
以使其进行替换。将第 138 行附近的部分替换为以下代码:
elseif expr.AstType == 'BinopExpr' then --line 138
if (expr.Op == '<') then
tok_it = tok_it + 1
out:appendStr('my_less(')
formatExpr(expr.Lhs)
out:appendStr(',')
formatExpr(expr.Rhs)
out:appendStr(')')
else
formatExpr(expr.Lhs)
appendStr( expr.Op )
formatExpr(expr.Rhs)
end
仅此而已。它会将 c=a*b<c+d
替换为 my_less(a*b,c+d)
。只需在 运行 时间推送所有代码即可。