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

構文でこれを機能させるには、次の 2 つの選択肢しかありません。

オプション 1: Lua コアにパッチを適用します。

これはおそらく非常に困難であり、将来的にはメンテナンスの悪夢になるでしょう。<最大の問題は、比較演算子、>、が bool 値==~=返すと Lua が非常に低いレベルで想定していることです。

Lua が生成するバイトコードは、実際にはすべての比較でジャンプします。たとえば、 のようなものc = 4 < 5は、より似たバイトコードにコンパイルされますif (4 < 5) then c = true else c = false end

でバイトコードがどのように見えるかを確認できますluac -l file.luac=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+dのようなものを置き換えますmy_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 に答える