【发布时间】:2018-10-03 10:41:41
【问题描述】:
这个问题类似于之前发布的问题How can I deep-compare 2 Lua tables, which may or may not have tables as keys?
问题是,那里的解决方案非常适合简单的深度比较。但是,它不能正确处理循环引用。更具体地说,如下:
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
local t1 = {}
t1[t1]=t1
t1.x = {[t1] = {1, 2, 3}}
local t2 = {}
local t3 = {}
t2[t3]=t2
t3[t2]=t3
t2.x = {[t3] = {1, 2, 3}}
t3.x = {[t2] = {1, 2, 3}}
print(table_eq(t1, t2))
--[[>
lua: deeptest.lua:15: stack overflow
stack traceback:
deeptest.lua:15: in function <deeptest.lua:3>
(...tail calls...)
deeptest.lua:26: in function <deeptest.lua:3>
(...tail calls...)
deeptest.lua:26: in function <deeptest.lua:3>
(...tail calls...)
deeptest.lua:26: in function <deeptest.lua:3>
(...tail calls...)
deeptest.lua:26: in function <deeptest.lua:3>
(...tail calls...)
deeptest.lua:26: in function <deeptest.lua:3>
(...tail calls...)
deeptest.lua:26: in function <deeptest.lua:3>
(...tail calls...)
deeptest.lua:26: in function <deeptest.lua:3>
(...tail calls...)
deeptest.lua:26: in function <deeptest.lua:3>
(...tail calls...)
deeptest.lua:26: in function <deeptest.lua:3>
(...tail calls...)
...
deeptest.lua:26: in function <deeptest.lua:3>
(...tail calls...)
deeptest.lua:26: in function <deeptest.lua:3>
(...tail calls...)
deeptest.lua:26: in function <deeptest.lua:3>
(...tail calls...)
deeptest.lua:26: in function <deeptest.lua:3>
(...tail calls...)
deeptest.lua:26: in function <deeptest.lua:3>
(...tail calls...)
deeptest.lua:26: in function <deeptest.lua:3>
(...tail calls...)
deeptest.lua:26: in function <deeptest.lua:3>
(...tail calls...)
deeptest.lua:26: in function <deeptest.lua:3>
(...tail calls...)
deeptest.lua:26: in function <deeptest.lua:3>
(...tail calls...)
deeptest.lua:62: in main chunk
[C]: in ?
--]]
产生堆栈溢出。如果不是因为堆栈溢出,它可能会产生误报(不是我可以测试的)。
我该如何处理这种情况? (它甚至可以处理吗?当我想到它时,这听起来像是计算机科学中一个未解决的问题......但我对此知之甚少)
当我说“结构平等”时,我指的是下表:
local t = {}
t[{}] = 1
t["1"] = {}
在结构上与下表不同:
local t = {}
local t2 = {}
t[t2] = 1
t["1"] = t2
而在“内容平等”中,它们是平等的。
测试用例:
local t1 = {}
t1[t1]=t1
t1.x = {[t1] = {1, 2, 3}}
local t2 = {}
local t3 = {}
t2[t3]=t2
t3[t2]=t3
t2.x = {[t3] = {1, 2, 3}}
t3.x = {[t2] = {1, 2, 3}}
assert(table_eq(t1, t2) == false)
assert(table_eq(t2, t3) == true)
local t4 = {}
t4[{}] = 1
t4["1"] = {}
local t5 = {}
local t6 = {}
t5[t6] = 1
t5["1"] = t6
assert(table_eq(t4, t5) == false)
【问题讨论】:
-
你能给出你对平等的定义吗?从您的示例中,我无法理解
table_eq(t2, t3)的正确答案应该是什么。 -
也许一种选择是将所有对象推入一个表中,然后断言它们都是浅相等的(以消除误报),然后进行深度比较?该解决方案可能仍然存在误报,但很容易使用当前算法。
-
@EgorSkriptunoff
table_eq(t2, t3)应该是...未定义。 t2 和 t3 指向同一对象的不同部分(即它们相互引用)。只有table_eq(t1, t2)(即具有完全隔离表的table_eq)才是重要的/应该是确定性的。 -
仍在尝试理解预期的比较... 浅比较足以确定两个表具有相同的键值对。看来您想要某种结构比较;包括,也许所有的空表都是等价的?
-
@TomBlodget 否。只有在系统内的引用相同时,空表才等同于空表。也就是说,给定两个空表 t1 和 t2,
table_eq(t1, t2)应该为真,但table_eq({a=t1, b=t2}, {a=t2, b=t2})应该为假。
标签: algorithm lua compare structural-equality