Lua: настройка метаметода __lt не работает так, как я хочу - PullRequest
5 голосов
/ 22 декабря 2011

Это мой первый вопрос к этому невероятному сообществу.

В эти дни я пишу модуль Lua для себя. Вот это минимальная часть кода с проблемой

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)

Если вы запустите этот маленький скрипт, вы увидите, что когда я использую функцию bf.compare напрямую, у меня есть то, что мне нужно. Когда я использую bf.compare через метаметод, он дает мне только «скалярное» значение true.

Есть предложения?

EDIT

Вот это вывод:

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

What I get
true
>Exit code: 0

Ответы [ 2 ]

4 голосов
/ 22 декабря 2011

В руководстве Lua указан этот псевдокод для __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

, если есть метаметод, тогда эта строка return not not h(op1, op2) возвращает только один символ) значение, возвращаемое обработчиком h, так как not является унарным оператором.В качестве второго эффекта он преобразует выходные данные обработчика в скаляр: not {} == false и not false == true.

Еще одна небольшая вещь, на которую следует обратить внимание: таблицы Lua всегда передаются по ссылке.Присвоение таблицы другой переменной просто приводит к копированию указателя.Следовательно, если вы делаете такие вещи, как:

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 голосов
/ 22 декабря 2011

Результат всех метаметодов сравнения автоматически приводится к логическому значению.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...