Go变量被覆盖(bug?)

时间:2013-11-29 02:07:53

标签: go

这里有点奇怪的。我的问题是,人们是否像我一样运行我的代码得到相同的结果?如果你这样做,这是我的代码的错(我通常是python程序员),还是golang中的错误?

系统信息:转到版本(1.1.2)linux x64(fedora 19)

关于代码的背景信息:我正在做的是找到从三角形顶部到底部的最高成本路线,这是来自project_euler 18和67

错误:我设置了一个名为pathA的变量,这是一个整数列表,加上一个新的int,用于从三角形中找到的新值 例如3,7,2追加8应该等于3,2,7,8 而且,确实如此! ......直到我设置pathB。 pathB设置正确但突然pathA与pathB的值相同。

tl; dr 当我设置另一个

时,一个变量被覆盖

我的代码如下:

package main

import (
    "fmt"
)

func extendPaths(triangle, prePaths [][]int) [][]int {
    nextLine := triangle[len(prePaths)]
    fmt.Println("#####PrePaths: ", prePaths)
    fmt.Println("#####nextLine: ", nextLine)

    postPaths := [][]int{{}}
    for i := 0; i < len(prePaths); i++ {
        route := prePaths[i]
        nextA := nextLine[i]
        nextB := nextLine[i+1]

        fmt.Println("Next A:", nextA, "Next B:", nextB, "\n")
        pathA := append(route, nextA)
        fmt.Println("pathA check#1:", pathA)
        pathB := append(route, nextB)
        fmt.Println("pathA check#2:", pathA, "\n")

        postPaths = append(postPaths, pathA)
        postPaths = append(postPaths, pathB)
    }
    postPaths = postPaths[1:]

    prePaths = [][]int{postPaths[0]}
    for i := 1; i < len(postPaths)-1; i += 2 {
        if getSum(postPaths[i]) > getSum(postPaths[i+1]) {
            prePaths = append(prePaths, postPaths[i])
        } else {
            prePaths = append(prePaths, postPaths[i+1])
        }
    }
    prePaths = append(prePaths, postPaths[len(postPaths)-1])
    return prePaths
}

func getSum(sumList []int) int {
    total := 0
    for i := 0; i < len(sumList); i++ {
        total += sumList[i]
    }
    return total
}

func getPaths(triangle [][]int) {
    prePaths := [][]int{{triangle[0][0]}}
    for i := 0; i < len(triangle)-1; i++ {
        prePaths = extendPaths(triangle, prePaths)
    }
}

func main() {
    triangle := [][]int{{3}, {7, 4}, {2, 4, 6}, {8, 5, 9, 3}}
    getPaths(triangle)
}

这给出了我的终端输出如下所示:

#####PrePaths:  [[3]]
#####nextLine:  [7 4]
Next A: 7 Next B: 4

pathA check#1: [3 7]
pathA check#2: [3 7]

#####PrePaths:  [[3 7] [3 4]]
#####nextLine:  [2 4 6]
Next A: 2 Next B: 4

pathA check#1: [3 7 2]
pathA check#2: [3 7 2]

Next A: 4 Next B: 6

pathA check#1: [3 4 4]
pathA check#2: [3 4 4]

#####PrePaths:  [[3 7 2] [3 7 4] [3 4 6]]
#####nextLine:  [8 5 9 3]
Next A: 8 Next B: 5

pathA check#1: [3 7 2 8]
pathA check#2: [3 7 2 5]

Next A: 5 Next B: 9

pathA check#1: [3 7 4 5]
pathA check#2: [3 7 4 9]

Next A: 9 Next B: 3

pathA check#1: [3 4 6 9]
pathA check#2: [3 4 6 3]

在这里你可以看到,在我设置pathA的最后4次中,它最初设置正确,但后来被pathB覆盖。

有没有人对此有任何想法?

修改

正如下面的评论所指出的,所需要的是制作新切片并从原件复制数据。这是使用http://blog.golang.org/go-slices-usage-and-internals稍微修改的代码完成的:

func AppendInt(slice []int, data ...int) []int {
    m := len(slice)
    n := m + len(data)
    if n > cap(slice) {
        newSlice := make([]int, (n+1)*2)
        copy(newSlice, slice)
        slice = newSlice
    }
    slice = slice[0:n]
    copy(slice[m:n], data)
    return slice
}

我还更改了另一侧的代码,我创建了切片路径A和路径B.这改为:

for i := 0; i < len(prePaths); i++ {

    nextA := nextLine[i]
    nextB := nextLine[i+1]

    pathA := AppendInt(prePaths[i], nextA)
    pathB := AppendInt(prePaths[i], nextB)

    postPaths = append(postPaths, pathA)
    postPaths = append(postPaths, pathB)
}

EDIT2:

早上很早,我在第一次编辑时犯了一个错误,我没有完全理解你的解决方案,经过一段时间的黑客攻击我最终到达那里:

此代码不起作用(pathA被覆盖):

for i := 0; i < len(prePaths); i++ {

    nextA := nextLine[i]
    nextB := nextLine[i+1]

    pathA := append(prePaths[i], nextA)
    pathB := append(prePaths[i], nextB)

    postPaths = append(postPaths, pathA)
    postPaths = append(postPaths, pathB)
}

此代码也不起作用(pathA被覆盖):

for i := 0; i < len(prePaths); i++ {

    newRoute := make([]int, len(prePaths[i]), (cap(prePaths[i])+1)*2)
    copy(newRoute, prePaths[i])

    nextA := nextLine[i]
    nextB := nextLine[i+1]

    pathA := append(newRoute, nextA)
    pathB := append(newRoute, nextB)

    postPaths = append(postPaths, pathA)
    postPaths = append(postPaths, pathB)
}

但是,如果我将上述2个场景混合到下面的代码中,它可以正常工作(pathA不会被覆盖):

for i := 0; i < len(prePaths); i++ {

    newRoute := make([]int, len(prePaths[i]), (cap(prePaths[i])+1)*2)
    copy(newRoute, prePaths[i])

    nextA := nextLine[i]
    nextB := nextLine[i+1]

    pathA := append(newRoute, nextA)
    pathB := append(prePaths[i], nextB)

    postPaths = append(postPaths, pathA)
    postPaths = append(postPaths, pathB)
}

所以,我的解决方案是制作数组的副本,并让它们都使用不同的数组。

1 个答案:

答案 0 :(得分:5)

切片基本上是由三件事组成的结构:

  1. 指向切片
  2. 中元素数组的指针
  3. 该数组的长度(“容量”)
  4. 实际存储在数组中的元素数(“长度”)
  5. 运行以下代码时:

    append(x, element)
    

    它执行以下操作:

    1. 检查扩展切片是否会超出底层阵列的容量。如果是,请分配较大的元素并将现有元素复制到新阵列,然后更新容量。
    2. 将新元素(或元素)写入数组的末尾并更新长度。
    3. 返回新切片。
    4. 在您的代码中,您有以下内容:

      pathA := append(route, nextA)
      pathB := append(route, nextB)
      

      现在有两种可能性:

      1. len(route) == cap(route),将分配一个新的支持数组,pathApathB具有独立的值。
      2. len(route) < cap(route),因此pathApathB最终共享相同的支持数组。数组中的最后一个元素将是nextB,因为该操作是第二次运行。
      3. 似乎第一种情况适用于循环的前几次迭代,之后你会遇到第二种情况。您可以通过手动为其中一个路径制作副本来避免这种情况(使用make()分配切片,然后使用copy()复制旧数据)。