Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lua: __lt metamethod setting is not working how I want

Tags:

lua

This is my first quesion to this incredible community.

In these days I'm writing a Lua module for myself. Here it is a minimum part of the code with the problem

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)

If you run this little script, you can see that when I use the function bf.compare directly I have what I need. When I use the bf.compare through the metamethod, it gives me only a "scalar" value of true.

Any suggestion?

EDIT

Here it is the output:

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

What I get
true
>Exit code: 0
like image 777
Azoun Avatar asked Dec 22 '11 14:12

Azoun


People also ask

What is a Metamethod Lua?

A metatable is a regular Lua table containing a set of metamethods, which are associated with events in Lua. Events occur when Lua executes certain operations, like addition, string concatenation, comparisons etc. Metamethods are regular Lua functions which are called when a specific event occurs.

How do metatables work Lua?

Metatables allow us to change the behavior of a table. For instance, using metatables, we can define how Lua computes the expression a+b , where a and b are tables. Whenever Lua tries to add two tables, it checks whether either of them has a metatable and whether that metatable has an __add field.

What is __ index in Lua?

The use of the __index metamethod for inheritance is so common that Lua provides a shortcut. Despite the name, the __index metamethod does not need to be a function: It can be a table, instead. When it is a function, Lua calls it with the table and the absent key as its arguments.


2 Answers

The Lua manual states this pseudo code for the __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

if there is a metamethod, then this line return not not h(op1, op2) returns only a singel (the first) value returned by the handler h, as not is a unary operator. As a second effect it converts the handler output to a scalar: not {} == false, and not false == true.

Another small thing to notice: Lua tables are always passed by reference. Assigning a table to another variable just results in copying the pointer. Hence if you do stuff like:

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!
like image 77
jpjacobs Avatar answered Oct 10 '22 23:10

jpjacobs


The result of all comparison metamethods is automatically cast to a boolean.

like image 2
Puppy Avatar answered Oct 10 '22 22:10

Puppy