为什么 go-cmp Equal() 说结构不是深度相等,即使所有字段都深度相等?
Why does go-cmp Equal() say that the structs are not deeply equal, even though all fields are deeply equal?
我一直在使用 reflect.DeepEqual
来深入比较使用循环指针的结构。由于这不适用于地图和更好的测试输出,我切换到 go-cmp
.
现在我必须注意,即使 cmp.Equal
应该是 reflect.DeepEqual
的直接替代品,但在前者正常工作的情况下,在这种情况下会有不同的结果,即使它实际上非常相等。
任何人都可以告诉我为什么在这种情况下结果不同,理想情况下如何解决?
Go playground 中的代码:https://play.golang.com/p/rLWKwMlAfwu
(更新为使用 fmt.Printf(),因为我无法在操场上获得 testing
运行)
diff 的输出:
StrongConnect() mismatch (-want +got):
&⟪ref#0⟫main.Edge{
StartNode: &⟪ref#1⟫main.Node{
Variable: 1,
- Low: &⟪ref#0: 0xc00005c120⟫(...),
+ Low: &⟪ref#0: 0xc00005c120⟫(...),
High: &{StartNode: &⟪ref#1⟫(...), EndNode: &{Variable: 2}, EdgeType: 1, Weight: 1},
},
EndNode: &{Variable: 2},
EdgeType: 0,
Weight: 1,
}
reflect.DeepEqual
is more lax than cmp.Equal
将结构与循环进行比较(并且可以说是不正确的)。
cmp.Equal
仅当图中的节点和边集相同时才认为重叠图等效。 注意:Node
和Edge
结构都是该图比较中的节点。
在您的示例中,2 graphs/structs 重叠但 wantEdge0
有一个额外的 Edge
结构作为根节点(前置)。
这里是你的数据结构中循环的简化表示:
wantEdge0 := &main.Edge{ // n0
StartNode: &main.Node{ // n1
Low: &main.Edge{} // n2
},
}
wantEdge0.StartNode.Low.StartNode = wantEdge0.StartNode // n1
got := wantEdge0.StartNode.Low // n2
因此有 2 个不同的周期:
wantEdge0 [n0] -> wantEdge0.StartNode [n1] -> got [n2] -> wantEdge0.StartNode [n1]
got [n2] -> wantEdge0.StartNode [n1] -> got [n2]
这里有一个简单的例子来说明 reflect.DeepEqual
和 cmp.Equal
之间的区别:
package main
import (
"fmt"
"reflect"
"github.com/google/go-cmp/cmp"
)
type Node struct {
Next *Node
Value int
}
func main() {
a0 := &Node{}
a1 := &Node{}
a2 := &Node{}
a0.Next = a1
a1.Next = a2
a2.Next = a1
b1 := &Node{}
b2 := &Node{}
b1.Next = b2
b2.Next = b1
fmt.Println("DeepEqual\tcmp.Equal")
fmt.Printf("\t%v\t%v\t\tIndependent graphs\n", reflect.DeepEqual(a1, b1), cmp.Equal(a1, b1))
fmt.Printf("\t%v\t%v\t\tSame graph, different root\n", reflect.DeepEqual(a1, a2), cmp.Equal(a1, a2))
fmt.Printf("\t%v\t%v\t\tSame graph prepend vs no prepend\n", reflect.DeepEqual(a0, a1), cmp.Equal(a0, a1))
}
输出:
$ ./compare
DeepEqual cmp.Equal
true true Independent graphs
true true Same graph, different root
true false Same graph prepend vs no prepend
解决方案:我建议为您的 want 结构和测试输入分配完全独立的结构。这样 want 和 got 结构就不会重叠,它们应该按预期进行比较。
参考文献:
我一直在使用 reflect.DeepEqual
来深入比较使用循环指针的结构。由于这不适用于地图和更好的测试输出,我切换到 go-cmp
.
现在我必须注意,即使 cmp.Equal
应该是 reflect.DeepEqual
的直接替代品,但在前者正常工作的情况下,在这种情况下会有不同的结果,即使它实际上非常相等。
任何人都可以告诉我为什么在这种情况下结果不同,理想情况下如何解决?
Go playground 中的代码:https://play.golang.com/p/rLWKwMlAfwu
(更新为使用 fmt.Printf(),因为我无法在操场上获得 testing
运行)
diff 的输出:
StrongConnect() mismatch (-want +got):
&⟪ref#0⟫main.Edge{
StartNode: &⟪ref#1⟫main.Node{
Variable: 1,
- Low: &⟪ref#0: 0xc00005c120⟫(...),
+ Low: &⟪ref#0: 0xc00005c120⟫(...),
High: &{StartNode: &⟪ref#1⟫(...), EndNode: &{Variable: 2}, EdgeType: 1, Weight: 1},
},
EndNode: &{Variable: 2},
EdgeType: 0,
Weight: 1,
}
reflect.DeepEqual
is more lax than cmp.Equal
将结构与循环进行比较(并且可以说是不正确的)。
cmp.Equal
仅当图中的节点和边集相同时才认为重叠图等效。 注意:Node
和Edge
结构都是该图比较中的节点。
在您的示例中,2 graphs/structs 重叠但 wantEdge0
有一个额外的 Edge
结构作为根节点(前置)。
这里是你的数据结构中循环的简化表示:
wantEdge0 := &main.Edge{ // n0
StartNode: &main.Node{ // n1
Low: &main.Edge{} // n2
},
}
wantEdge0.StartNode.Low.StartNode = wantEdge0.StartNode // n1
got := wantEdge0.StartNode.Low // n2
因此有 2 个不同的周期:
wantEdge0 [n0] -> wantEdge0.StartNode [n1] -> got [n2] -> wantEdge0.StartNode [n1]
got [n2] -> wantEdge0.StartNode [n1] -> got [n2]
这里有一个简单的例子来说明 reflect.DeepEqual
和 cmp.Equal
之间的区别:
package main
import (
"fmt"
"reflect"
"github.com/google/go-cmp/cmp"
)
type Node struct {
Next *Node
Value int
}
func main() {
a0 := &Node{}
a1 := &Node{}
a2 := &Node{}
a0.Next = a1
a1.Next = a2
a2.Next = a1
b1 := &Node{}
b2 := &Node{}
b1.Next = b2
b2.Next = b1
fmt.Println("DeepEqual\tcmp.Equal")
fmt.Printf("\t%v\t%v\t\tIndependent graphs\n", reflect.DeepEqual(a1, b1), cmp.Equal(a1, b1))
fmt.Printf("\t%v\t%v\t\tSame graph, different root\n", reflect.DeepEqual(a1, a2), cmp.Equal(a1, a2))
fmt.Printf("\t%v\t%v\t\tSame graph prepend vs no prepend\n", reflect.DeepEqual(a0, a1), cmp.Equal(a0, a1))
}
输出:
$ ./compare
DeepEqual cmp.Equal
true true Independent graphs
true true Same graph, different root
true false Same graph prepend vs no prepend
解决方案:我建议为您的 want 结构和测试输入分配完全独立的结构。这样 want 和 got 结构就不会重叠,它们应该按预期进行比较。
参考文献: