3

我试图找到一种使用标准<运算符在 Lua 中进行逐个元素比较的方法。例如,这是我想做的:

a = {5, 7, 10}
b = {6, 4, 15}
c = a < b -- should return {true, false, true}

我已经有代码用于加法(和减法、乘法等)。我的问题是 Lua 强制将比较结果与布尔值进行比较。我不想要一个布尔值,我想要一个表格作为比较的结果。

到目前为止,这是我的代码,加法有效,但小于比较不起作用:

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方法。我尝试了它并且它有效,但我真的想使用<少于,而不是使用错误符号的黑客。

谢谢!

4

4 回答 4

4

您只有两种选择来使用您的语法:

选项 1:修补 Lua 核心。

这可能会非常困难,而且将来会成为维护的噩梦。最大的问题是 Lua 在非常低的层次上假设比较运算符<, >, ==,~=返回一个 bool 值。

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 之类的东西)。

首先,编写一个可用于比较任何事物的函数。这里的想法是,如果它是一张表格,则进行特殊比较,但<其他一切都使用。

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

a<b现在我们需要做的就是用.替换每个小于运算符my_less(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+dmy_less(a*b,c+d). 只需在运行时推送所有代码。

于 2016-04-18T01:07:09.207 回答
3

Lua 中的比较返回一个布尔值。

除了改变 Lua 的核心,你无能为力。

于 2016-04-16T02:00:24.433 回答
1

你能忍受有点冗长的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)
于 2016-04-16T19:38:07.380 回答
0

正如其他人已经提到的那样,没有直接的解决方案。但是,通过使用类似 Python 的通用 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
于 2016-04-16T13:25:12.927 回答