我正在尝试动手实践,发现它的行为不符合预期。
我试图获取diff大小的n个数组的笛卡尔积。输出不正确。
这是我的代码
func main() {
sl1 := []int{1,2,3}
sl2 := []int{4}
sl3 := []int{5,6}
sl4 := []int{8,9}
sl := [][]int{sl1,sl2,sl3,sl4}
res := cartesianMain(sl)
fmt.Println(res)
}
func cartesianMain(a [][]int) [][]int {
res := [][]int{}
for i:=0;i<len(a[0]) ;i++{
res = append(res,[]int{a[0][i]})
}
for i:= 1;i<len(a) ;i++{
res = cartesianProduct(res,a[i])
}
return res;
}
func cartesianProduct(a [][]int, b []int) [][]int {
result := [][]int{}
for _,v1 := range b {
for _,v2 := range a {
result = append(result, append(v2,v1))
}
}
return result
}
实际输出:
[[1 4 5 9] [2 4 5 9] [3 4 5 9] [1 4 6 9] [2 4 6 9] [3 4 6 9] [1 4 5 9] [2 4 5 9] [3 4 5 9] [1 4 6 9] [2 4 6 9] [3 4 6 9]]
预期输出: 如果看到sl4的第一个元素8被9覆盖。 正确答案将是:
[[1 4 5 8] [2 4 5 8] [3 4 5 8] [1 4 6 8] [2 4 6 8] [3 4 6 8] [1 4 5 9] [2 4 5 9] [3 4 5 9] [1 4 6 9] [2 4 6 9] [3 4 6 9]]
答案 0 :(得分:2)
这是因为append
的工作方式。在Go中,slice是具有3个属性的标头:Len
,Cap
和Ptr
。 Len
是它本身的切片的长度,Cap
是切片的未扩展数组(内存)的容量,Ptr
是指向该数组的指针。
当append
追加到基础数组中没有更多空间的切片时,它会分配一个新的基础数组,该数组的空间比需要的多,并将原始切片的内容复制到其中,然后添加新元素。
然后,当append
追加到Cap
> Len
的切片时,即在已分配的内存中仍然有足够的空间时,append
会保留基础数组和复制要添加到a[Len+1]
的元素(其中a是基础数组)。当两个或多个片共享基础内存时,这将导致问题。
经验法则是经常检查复制切片的必要性,以避免不必要的底层数组共享。
要解决此问题,请将result = append(result, append(v2,v1))
更改为result = append(result, append([]int{}, append(v2, v1)...))
。
另请参阅:https://blog.golang.org/slices
注1:append([]int{},append(v2,v1...))
是copy
和apply
的快捷方式。有关切片的更多技巧,请访问:https://github.com/golang/go/wiki/SliceTricks。
注2:在Go中,nil是slice的有效值。因此您可以通过将cartesianMain
设置为res
来消除[]int{nil}
中分割的分隔。
注3:为了获得更好的性能和更少的分配,最好为已知片设置容量(或长度)。在cartesianProduct
中,您可以使用result := make([][]int, 0, len(a)*len(b))
。