Edster
Edster

Reputation: 163

The most elegant solution using Lua tables to this snippet of Python

I'm learning Lua and coming from Python Lua tables seem rather convoluted, the simple example below is so elegant but translating this into Lua is difficult for me, as Lua has no concept of tuples.

So I'm looking for the best Lua solution for this snippet

a = [(1, 1), (2, 2), (3, 3), (4, 4)]
if (3, 3) in a:
    print("Yay!")
else:
    print("Nay!")

Upvotes: 1

Views: 502

Answers (1)

Curtis Fenner
Curtis Fenner

Reputation: 1403

A pair is just like length-2 list, so you could express a simply as

a = {{1, 1}, {2, 2}, {3, 3}, {4, 4}}

The tricky part here is rather that Python's in compares objects by their value rather than their identity. That is, in Python, (1,2) in [(1,2)] but (1,2) is not (1,2).

Lua has no notion of "value" equality (except for strings and numbers, which don't have identities).

You could override the behavior of == by setting the __eq metametod. Unfortunately, Lua doesn't have a function for searching a table for a value equal to some query, so it might be overkill.

Directly, you could write a "contains pair" function that works on the a as defined above like this:

function containsPair(list, pair)
    -- Find a key of list with a value equal to `pair` (as a pair)
    for k, v in ipairs(list) do
        if v[1] == pair[1] and v[2] == pair[2] then
            return k
        end
    end
end

if containsPair(a, {3, 3}) then
    ......
end

You could make it more general by passing a function do the comparison (or equivalently, just use == but implement the __eq metamethod):

function containsLike(list, lhs, eq)
    -- Find a key of list with a value equal to lhs
    for k, lhs in ipairs(list) do
        if eq(lhs, rhs) then
            return k
        end
    end
end

function pairEq(a, b)
    return a[1] == b[1] and a[2] == b[2]
end

if containsLike(list, {3, 3}, pairEq) then
    ......
end

If what you're really after is a set of pairs, you could instead use a "two-dimensional map" (a map of maps):

a = {}
a[1] = {}
a[1][1] = true
a[2] = {}
a[2][2] = true
a[3] = {}
a[3][3] = true

if a[3] and a[3][3] then
    ......
end

Checking that rows have already been created could be cumbersome. You can use metatables to imitate Python's defaultdict and clean this up:

function default(f)
    return setmetatable({}, {
        __index = function(self, k)
            -- self[k] is nil, but was asked for.
            -- Let's assign it to the default value:
            self[k] = f()

            -- and return the new assignment:
            return self[k]
        end,
    })
end

local a = default(function() return {} end)
a[1][1] = true
a[2][2] = true
a[3][3] = true

if a[3][3] then
    ......
end

Upvotes: 2

Related Questions