2011-12-22 12 views
5

Questa è la mia prima domanda a questa incredibile comunità.Lua: __lt metamethod setting non funziona come voglio

In questi giorni sto scrivendo un modulo Lua per me stesso. Qui si tratta di una minima parte del codice con il 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) 

Se si esegue questo piccolo script, si può vedere che quando uso la funzione bf.compare direttamente Ho quello che mi serve. Quando uso bf.compare attraverso il metamethod, mi dà solo un valore "scalare" di true.

Qualche suggerimento?

EDIT

Qui è l'uscita:

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

What I get 
true 
>Exit code: 0 
+0

Non riesco a vederlo affatto, perché non mostri l'output. – Puppy

+1

+1 per dare effettivamente codice funzionante. – jpjacobs

risposta

4

I Lua manual stati questo codice pseudo per 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 

se c'è un metamethod, allora questa linea return not not h(op1, op2) restituisce solo un valore singel (il primo) restituito dall'handler h, come not è un operatore unario. Come secondo effetto converte l'output del gestore in uno scalare: not {} == false e not false == true.

Un'altra piccola cosa da notare: le tabelle Lua vengono sempre passate per riferimento. L'assegnazione di una tabella a un'altra variabile ha come risultato solo la copia del puntatore. Quindi se fai cose del genere:

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

Il risultato di tutti metamethods confronto è gettata automaticamente ad un valore booleano.