luaoperatorslua-tableevaluationmetatable

Nesting of operators in Lua does not give correct answer


The following code defines sort of custom operator imp. It works almost correctly.

    local function _imp(x, y)
      if x == 1 and y == 0 then return 0
        else return 1 end
    end
    
    local tmp = {}
    local imp = {}
    
    local imp_mult= {
      __mul = function(a,b)
        if b==imp then
          tmp[1]=a
          return tmp
        elseif a == tmp then
          return _imp(tmp[1], b)
        end
      end
    }
    
    setmetatable(tmp, imp_mult)
    setmetatable(imp, imp_mult)
    _G.imp = imp

print("0 imp 0 evaluates to ", 0*imp*0)
print("1 imp 0 evaluates to", 1*imp*0)
print("0 imp (1 imp 0) evaluates to", 0*imp*((1*imp*0))) 

The above code outputs to

0 imp 0 evaluates to 1
1 imp 0 evaluates to 0
0 imp (1 imp 0) evaluates to 0

The first two answers are correct. However the expected answer for last expression is 1 as 0*imp*0 evaluates to 1 and 1*imp*0 evaluates to 0. Where is the mistake? How the definition of imp_mult can be improved to get expected answer?


Solution

  • The value of tmp[1] will be overwritten. Instead of 1, you should use a counter variable that is incremented before the operation is done (x * imp), and decremented when an operation is done (x * imp * y).

    local function _imp(x, y)
        if x == 1 and y == 0 then return 0
        else return 1 end
    end
    
    local tmp = {}
    local imp = {}
    
    local depth = 0
    
    local imp_mult = {
        __mul = function(a,b)
            if b==imp then
                depth = depth + 1
                tmp[depth] = a
                return tmp
            elseif a == tmp then
                local v = _imp(tmp[depth], b)
                depth = depth - 1
                return v
            end
        end
    }
        
    setmetatable(tmp, imp_mult)
    setmetatable(imp, imp_mult)
    _G.imp = imp
    
    print("0 imp 0 evaluates to ", 0*imp*0)
    print("1 imp 0 evaluates to", 1*imp*0)
    print("0 imp (1 imp 0) evaluates to", 0*imp*((1*imp*0))) 
    

    You can change the place of the assignment to use the 0th index if you want to.