算法 – 如何深入比较2个Lua表,这些表可能有也可能没有表作为键?

(也发布在Lua邮件列表上)

所以我一直在编写深度复制算法,我想测试它们是否按照我希望的方式工作.虽然我可以访问原始>复制映射,但我想要一个通用的深度比较算法,它必须能够比较表键(表作为键?).

我的深拷贝算法在这里是可用的:https://gist.github.com/SoniEx2/fc5d3614614e4e3fe131(它不是很有组织,但有3个,一个使用递归调用,另一个使用todo表,另一个模拟调用栈(非常难看)但5.1兼容的方式))

递归版:

local function deep(inp,copies)
  if type(inp) ~= "table" then
    return inp
  end
  local out = {}
  copies = (type(copies) == "table") and copies or {}
  copies[inp] = out -- use normal assignment so we use copies' Metatable (if any)
  for key,value in next,inp do -- skip Metatables by using next directly
    -- we want a copy of the key and the value
    -- if one is not available on the copies table,we have to make one
    -- we can't do normal assignment here because Metatabled copies tables might set Metatables

    -- out[copies[key] or deep(key,copies)]=copies[value] or deep(value,copies)
    rawset(out,copies[key] or deep(key,copies),copies[value] or deep(value,copies))
  end
  return out
end

编辑:我发现这样的东西并没有真正处理表作为键:http://snippets.luacode.org/snippets/Deep_Comparison_of_Two_Values_3(下面的代码片段的副本)

function deepcompare(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 deepcompare(v1,v2) then return false end
  end
  for k2,v2 in pairs(t2) do
    local v1 = t1[k2]
    if v1 == nil or not deepcompare(v1,v2) then return false end
  end
  return true
end

序列化也不是一种选择,因为序列化的顺序是“随机的”.

解决方法

正如其他人所说,这在很大程度上取决于你对等价的定义.
如果你想要这是真的:
local t1 = {[{}] = {1},[{}] = {2}}
local t2 = {[{}] = {1},[{}] = {2}}
assert( table_eq(t1,t2) )

如果你这样做,那么每当t1中的键是一个表时,你就必须这样做
检查它与t2中每个表键的等价性,然后逐个尝试
一.这是一种方法(为了便于阅读而省略了可用的东西).

function table_eq(table1,table2)
   local avoid_loops = {}
   local function recurse(t1,t2)
      -- compare value types
      if type(t1) ~= type(t2) then return false end
      -- Base case: compare simple values
      if type(t1) ~= "table" then return t1 == t2 end
      -- Now,on to tables.
      -- First,let's avoid looping forever.
      if avoid_loops[t1] then return avoid_loops[t1] == t2 end
      avoid_loops[t1] = t2
      -- copy keys from t2
      local t2keys = {}
      local t2tablekeys = {}
      for k,_ in pairs(t2) do
         if type(k) == "table" then table.insert(t2tablekeys,k) end
         t2keys[k] = true
      end
      -- Let's iterate keys from t1
      for k1,v1 in pairs(t1) do
         local v2 = t2[k1]
         if type(k1) == "table" then
            -- if key is a table,we need to find an equivalent one.
            local ok = false
            for i,tk in ipairs(t2tablekeys) do
               if table_eq(k1,tk) and recurse(v1,t2[tk]) then
                  table.remove(t2tablekeys,i)
                  t2keys[tk] = nil
                  ok = true
                  break
               end
            end
            if not ok then return false end
         else
            -- t1 has a key which t2 doesn't have,fail.
            if v2 == nil then return false end
            t2keys[k1] = nil
            if not recurse(v1,v2) then return false end
         end
      end
      -- if t2 has a key which t1 doesn't have,fail.
      if next(t2keys) then return false end
      return true
   end
   return recurse(table1,table2)
end

assert( table_eq({},{}) )
assert( table_eq({1,2,3},{1,3}) )
assert( table_eq({1,3,foo = "fighters"},{["foo"] = "fighters",1,3}) )
assert( table_eq({{{}}},{{{}}}) )
assert( table_eq({[{}] = {1},[{}] = {2}},{[{}] = {1},[{}] = {2}}) )
assert( table_eq({a = 1,[{}] = {}},{[{}] = {},a = 1}) )
assert( table_eq({a = 1,[{}] = {1},{[{}] = {2},a = 1,[{}] = {1}}) )

assert( not table_eq({1,4},3}) )
assert( not table_eq({1,{["foo"] = "bar",3}) )
assert( not table_eq({{{}}},{{{{}}}}) )
assert( not table_eq({[{}] = {1},[{}] = {2},[{}] = {3}}) )
assert( not table_eq({[{}] = {1},[{}] = {3}}) )

相关文章

1.github代码实践源代码是lua脚本语言,下载th之后运行thmai...
此文为搬运帖,原帖地址https://www.cnblogs.com/zwywilliam/...
Rime输入法通过定义lua文件,可以实现获取当前时间日期的功能...
localfunctiongenerate_action(params)localscale_action=cc...
2022年1月11日13:57:45 官方:https://opm.openresty.org/官...
在Lua中的table(表),就像c#中的HashMap(哈希表),key和...