2011-12-22 13 views
5

To jest moje pierwsze pytanie do tej niesamowitej społeczności.Lua: Ustawienie metameth nie działa tak, jak chcę

W tych dniach piszę moduł Lua dla siebie. Tutaj jest to minimalna część kodu z problemem

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) 

Jeśli prowadzisz ten mały skrypt, można zobaczyć, że kiedy używam bf.compare funkcji bezpośrednio Mam to, czego potrzebuję. Kiedy używam BF.compare przez metametod, daje mi to tylko "skalarną" wartość true.

Jakieś sugestie?

EDIT

Tutaj jest wyjście:

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

What I get 
true 
>Exit code: 0 
+0

Naprawdę nie widzę tego w ogóle, ponieważ nie wyświetla się wyników. – Puppy

+1

+1 za podanie działającego kodu. – jpjacobs

Odpowiedz

4

W Lua manual stany ten pseudo kod do __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 

jeśli istnieje metamethod, to linia return not not h(op1, op2) zwraca tylko pojedynczą (pierwszą) wartość zwróconą przez handlerkę h, as not jest operatorem jednoargumentowym. Jako drugi efekt konwertuje dane wyjściowe modułu obsługi na skalar: not {} == false i not false == true.

Kolejna drobna rzecz do zauważenia: tabele Lua są zawsze przekazywane przez odniesienie. Przypisanie tabeli do innej zmiennej powoduje tylko skopiowanie wskaźnika. Stąd, jeśli nie rzeczy jak:

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

Wynikiem wszystkich metamethods porównanie jest automatycznie rzutować na wartość logiczną.

Powiązane problemy