2011-12-22 5 views
5

Esta es mi primera pregunta a esta increíble comunidad.Lua: __lt configuración de metametodo no funciona como quiero

En estos días estoy escribiendo un módulo Lua para mí. Aquí es una mínima parte del código con el problema

mt = {} 

bf = {} 

------------ 
-- bf.new -- 
------------ 
function bf.new(A) 

    local out = A 
    setmetatable(out,mt) 
    return out 

end 

----------------- 
-- bf.tostring -- 
----------------- 
function bf.tostring(A) 

    local typeA = type(A) 
    local out = "" 
    if typeA=="number" or typeA=="boolean" then 
     print(tostring(A)) 
    elseif typeA=="table" then 
     local col = "" 
     local m  = #A 
     local typeA1 = type(A[1]) 
     for i=1,m do 
      if typeA1=="table" then 
       n = #A[1] 
       for j=1,n do 
        out = out.." "..tostring(A[i][j]) 
        if j==n then 
         out = out.."\n" 
        end 
       end 
      elseif (typeA1=="number" or typeA1=="boolean") then 
       row = row.." "..tostring(A[i][j]) 
      end 
     end 
    end 
    return out 
end 

----------------------------- 
-- bf.compare(A,logical,B) -- 
----------------------------- 
function bf.compare(A,logical,B) 

    if (logical~="<" and 
     logical~=">" and 
     logical~="<=" and 
     logical~=">=" and 
     logical~="==" and 
     logical~="~=") then 
     error("second input input must be a logical operator written into a string") 
    end 
    local out = {} 
    local ind = {} 
    local count = 0 
    if type(B)=="number" then 
     if type(A[1])=="table" then 
      for i=1,#A do 
       out[i] = {} 
       for j=1,#A[1] do 
        loadstring("cond ="..A[i][j]..logical..B)() 
        if cond then 
         out[i][j] = true 
         count  = count+1 
         ind[count] = (i-1)*#A[1]+j 
        else 
         out[i][j] = false 
        end 
       end 
      end 
     elseif type(A[1])=="number" then 
      for j=1,#A do 
       loadstring("cond ="..A[j]..logical..B)() 
       if cond then 
        out[j]  = true 
        count  = count+1 
        ind[count] = j 
       else 
        out[j]  = false 
       end 
      end 
     end 
    else 
     if (type(A[1])=="table" and type(B[1])=="table") then 
      if (#A==#B and #A[1]==#B[1]) then 
       for i=1,#A do 
        out[i] = {} 
        for j=1,#A[1] do 
         loadstring("cond ="..A[i][j]..logical..B[i][j])() 
         if cond then 
          out[i][j] = true 
          count  = count+1 
          ind[count] = (i-1)*#A[1]+j 
         else 
          out[i][j] = false 
         end 
        end 
       end 
      else 
       error("The comparison can be done between ".. 
         "two matrix with same dimension ".. 
         "or between a matrix with a scalar value") 
      end 
     elseif (type(A[1])=="number" and type(B[1])=="number") then 
      if (#A==#B) then 
       for j=1,#A do 
        loadstring("cond ="..A[j]..logical..B[j])() 
        if cond then 
         out[j] = true 
         count  = count+1 
         ind[count] = j 
        else 
         out[j] = false 
        end 
       end 
      else 
       error("Comparison between ".. 
         "two vector with different dimension") 
      end 
     else 
      error("The comparison can be done between ".. 
        "two matrix with same dimension ".. 
        "or between a matrix with a scalar value") 
     end 
    end 
    return setmetatable(out,mt)--,ind 

end 

------------------------ 
-- metamethod setting -- 
------------------------ 
mt.__tostring = bf.tostring 
mt.__lt  = function(A,B) return bf.compare(A,"<",B) end 

-------------------------- 
-- use of my metamethod -- 
-------------------------- 
A = bf.new{{1,2,3,4},{5,6,7,8},{9,10,11,12}} 
B = bf.new{{3,6,1,8},{1,3,87,20},{11,2,5,7}} 
C1 = bf.compare(A,"<",B) 
C2 = A<B 
print("What I want") 
print(C1) 
print("What I get") 
print(C2) 

Si ejecuta este pequeño script, se puede ver que cuando se utiliza la función directamente bf.compare tengo lo que necesito. Cuando uso el bf.compare a través del meta-método, me da solo un valor "escalar" de verdadero.

¿Alguna sugerencia?

EDITAR

aquí está la salida:

What I want 
true true false true 
false false true true 
true false false false 

What I get 
true 
>Exit code: 0 
+0

Realmente no puedo ver eso, porque no muestra la salida. – Puppy

+1

+1 por dar realmente código de trabajo. – jpjacobs

Respuesta

4

Los Lua manual estados este pseudo código para la __lt metamethod:

function lt_event (op1, op2) 
     if type(op1) == "number" and type(op2) == "number" then 
     return op1 < op2 -- numeric comparison 
     elseif type(op1) == "string" and type(op2) == "string" then 
     return op1 < op2 -- lexicographic comparison 
     else 
     local h = getbinhandler(op1, op2, "__lt") 
     if h then 
      return not not h(op1, op2) 
     else 
      error(···) 
     end 
     end 
    end 

si hay un metamétodo, entonces esta línea return not not h(op1, op2) devuelve solo un valor singel (el primero) devuelto por el controlador h, como not es un operador unario. Como segundo efecto, convierte la salida del controlador a un escalar: not {} == false y not false == true.

Otra pequeña cosa para notar: las tablas Lua siempre se pasan por referencia. Asignar una tabla a otra variable solo resulta en copiar el puntero. Por lo tanto si lo hace cosas como:

function myFun(A) 
    local out=A 
    out[1]='bar' 
    return out 
end 
A={'foo',1,2,3} 
B=myFun(A) 
print(table.concat(B,', ')) -- OK 
print(table.concat(A,', ')) -- A also changed, because: 
print(A,B) -- they are the same table! 
2

El resultado de todas metamétodos comparación se echa automáticamente a un valor lógico.

Cuestiones relacionadas