问题 如何检查两个表(对象)在Lua中是否具有相同的值


我想检查两个表是否在Lua中具有相同的值,但是没有找到方法。

我用的是运营商 ==,它似乎只是检查相同的对象,但不是表中的元素。

如果我有两张桌子,

a={}
b={}

的价值 a==b 是 false

但如果

a={}
b=a

的价值 a==b 是 true

我想知道是否有办法检查Lua中具有相同元素的两个表。是否有像内置功能 table.equals() 去检查?


6709
2017-12-02 09:54


起源

请记住,这2个案例实际上是不同的。在第二种情况下,如果你愿意 a.test=true b.test将是相同的 - Ivo Beckers
了解它的关键是Lua按引用比较表,而不是内容。您需要自己比较内容。 - Yu Hao
好的我明白了。我想知道在python中可能有不同的运算符,如'=='和'is'。我只是想在两个简单的表之间进行比较。所以我先将它们转移到json,然后比较字符串。谢谢。 - LetsOMG


答案:


没有用于按内容比较表的内置函数。

你必须自己写。您需要决定是要逐个或深入地按内容比较表。 看到 https://web.archive.org/web/20131225070434/http://snippets.luacode.org/snippets/Deep_Comparison_of_Two_Values_3 一些想法。


6
2017-12-02 10:47





我提供了一些改进 Rutrus 解。

function equals(o1, o2, ignore_mt)
    if o1 == o2 then return true end
    local o1Type = type(o1)
    local o2Type = type(o2)
    if o1Type ~= o2Type then return false end
    if o1Type ~= 'table' then return false end

    if not ignore_mt then
        local mt1 = getmetatable(o1)
        if mt1 and mt1.__eq then
            --compare using built in method
            return o1 == o2
        end
    end

    local keySet = {}

    for key1, value1 in pairs(o1) do
        local value2 = o2[key1]
        if value2 == nil or equals(value1, value2, ignore_mt) == false then
            return false
        end
        keySet[key1] = true
    end

    for key2, _ in pairs(o2) do
        if not keySet[key2] then return false end
    end
    return true
end

请注意,此解决方案不考虑自引用。你可以使用pequals(下面)。当你的代码中有一些技巧时,这很有用。 但是不要使用这种方法进行定期检查!它慢了。此外,如果您的对象具有自引用,则应重新分析您的结构。自我引用可能是糟糕架构的标志。

local function internalProtectedEquals(o1, o2, ignore_mt, callList)
    if o1 == o2 then return true end
    local o1Type = type(o1)
    local o2Type = type(o2)
    if o1Type ~= o2Type then return false end
    if o1Type ~= 'table' then return false end

    -- add only when objects are tables, cache results
    local oComparisons = callList[o1]
    if not oComparisons then
        oComparisons = {}
        callList[o1] = oComparisons
    end
    -- false means that comparison is in progress
    oComparisons[o2] = false

    if not ignore_mt then
        local mt1 = getmetatable(o1)
        if mt1 and mt1.__eq then
            --compare using built in method
            return o1 == o2
        end
    end

    local keySet = {}
    for key1, value1 in pairs(o1) do
        local value2 = o2[key1]
        if value2 == nil then return false end

        local vComparisons = callList[value1]
        if not vComparisons or vComparisons[value2] == nil then
            if not internalProtectedEquals(value1, value2, ignore_mt, callList) then
                return false
            end
        end

        keySet[key1] = true
    end

    for key2, _ in pairs(o2) do
        if not keySet[key2] then
            return false
        end
    end

    -- comparison finished - objects are equal do not compare again
    oComparisons[o2] = true
    return true
end

function pequals(o1, o2, ignore_mt)
    return internalProtectedEquals(o1, o2, ignore_mt, {})
end

你也可以分析 在lua wiki上的CompareTables


6
2017-09-18 20:47





顺便说一下,我检查了@lhf链接并且坏了,我找到了这个有用的例子:

function is_table_equal(t1,t2,ignore_mt)
   local ty1 = type(t1)
   local ty2 = type(t2)
   if ty1 ~= ty2 then return false end
   -- non-table types can be directly compared
   if ty1 ~= 'table' and ty2 ~= 'table' then return t1 == t2 end
   -- as well as tables which have the metamethod __eq
   local mt = getmetatable(t1)
   if not ignore_mt and mt and mt.__eq then return t1 == t2 end
   for k1,v1 in pairs(t1) do
      local v2 = t2[k1]
      if v2 == nil or not is_table_equal(v1,v2) then return false end
   end
   for k2,v2 in pairs(t2) do
      local v1 = t1[k2]
      if v1 == nil or not is_table_equal(v1,v2) then return false end
   end
   return true
end

1
2018-06-10 13:12