2016-04-16 4 views
3

Я пытаюсь найти способ сравнения элементов по элементу в Lua с использованием стандартного оператора <. Например, вот что я хотел бы сделать:Элемент сравнения элементов в 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. Я попробовал, и это работает, но я действительно хочу использовать < меньше, чем вместо взлома с использованием неправильного символа.

Спасибо!

+2

Зачем вам нужен оператор? не можете ли вы просто использовать обычную функцию? – Piglet

+1

@ Piglet Я делаю DSL. Я хочу иметь возможность делать что-то вроде 'a * (b + 5)/c jenny

ответ

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). Просто загрузите весь свой код во время выполнения.

+0

Спасибо! Я дам ваш код попробовать и посмотреть, как он работает. Мне нравится идея, так как она оставляет меня с синтаксисом, который я хочу, что очень важно. – jenny

3

Сравнения в Lua возвращают логическое значение.

Вы ничего не можете поделать, если не изменить ядро ​​Lua.

+0

Спасибо, я увидел, что в руководстве. Думаю, я надеюсь, что смогу подготовить код или что-то еще. Могу ли я сделать это с помощью MetaLua? – jenny

+0

Любые советы о том, с чего начать с исправления Lua. Похоже, что операторы сравнения ничего не возвращают внутри, все они интерпретируются как прыжки. Даже в случае «a = 1 <2» Lua использует условный переход для того, чтобы присвоить либо «true», либо «false». – jenny

+0

@jenny. Это не просто. – lhf

0

Как уже упоминалось, прямолинейного решения нет. Тем не менее, с использованием функции общего Python, как молния(), например, как показано ниже, вы можете упростить задачу, например, так:

-------------------------------------------------------------------------------- 
-- 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 
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) 
+0

Я просто хотел сказать, что твоя идея блестящая. Это не то, что я мог бы придумать. Очень креативно! Я не уверен, что смогу использовать его, хотя. Я делаю DSL, и я думаю, что такого рода синтаксис будет путать людей, например, кто-то попытается: 'c = v ((a jenny

+0

Что означает '(a

+0

Это будет поэтапное сравнение каждого элемента с ложным, например. '{false, true, false} == false' вернет' {true, false, true} '. Опять же, у меня нет проблем с вычислением, просто нужно помочь сделать синтаксис приемлемым. – jenny

Смежные вопросы