Go变量被覆盖(bug?)

huangapple go评论77阅读模式
英文:

Go variables being overwritten (bug?)

问题

有点奇怪的问题。我的问题是,其他人运行我的代码是否会得到与我相同的结果?如果是这样,这是我的代码的问题(我通常是一个Python程序员),还是Golang的一个错误?

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

代码背景信息: 我正在寻找从三角形顶部到底部的最高成本路径,这是来自project_euler 18和67的问题。

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

简而言之,当我设置另一个变量时,一个变量被覆盖。

我的代码如下:

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
}

我还更改了另一侧的代码,即我创建了切片pathA和pathB的代码。更改如下:

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)
}

编辑2:

这里的时间还很早,我在第一个编辑中犯了一个错误,我没有完全理解你的解决方案,经过一些尝试,我最终明白了:

这段代码不起作用(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)
}

然而,如果我将上述两种情况混合到下面的代码中,它就可以正常工作(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)
}

所以,我的解决方案是创建一个数组的副本,并使它们都使用不同的副本。

英文:

So bit of a weird one here. My question is, do people get the same results from running my code as I do? And if you do, is it a fault of my code (I'm a python programmer usually), or a bug in golang?

System info: Go version (1.1.2) linux x64 (fedora 19)

Background info on the code: What I'm doing is finding the highest cost route from the top of a triangle to the bottom, this is from project_euler 18 and 67

The bug: I set a variable called pathA, this is an integer list, plus a new int for the new value found from the triangle
e.g. 3, 7, 2 append 8 should equal 3, 2, 7, 8
and, it does! ... until I set pathB. pathB gets set correctly however suddenly pathA is the same value as pathB.

tl;dr one variable is being overwritten when I set another

My code is as follows:

package main
import (
&quot;fmt&quot;
)
func extendPaths(triangle, prePaths [][]int) [][]int {
nextLine := triangle[len(prePaths)]
fmt.Println(&quot;#####PrePaths: &quot;, prePaths)
fmt.Println(&quot;#####nextLine: &quot;, nextLine)
postPaths := [][]int{{}}
for i := 0; i &lt; len(prePaths); i++ {
route := prePaths[i]
nextA := nextLine[i]
nextB := nextLine[i+1]
fmt.Println(&quot;Next A:&quot;, nextA, &quot;Next B:&quot;, nextB, &quot;\n&quot;)
pathA := append(route, nextA)
fmt.Println(&quot;pathA check#1:&quot;, pathA)
pathB := append(route, nextB)
fmt.Println(&quot;pathA check#2:&quot;, pathA, &quot;\n&quot;)
postPaths = append(postPaths, pathA)
postPaths = append(postPaths, pathB)
}
postPaths = postPaths[1:]
prePaths = [][]int{postPaths[0]}
for i := 1; i &lt; len(postPaths)-1; i += 2 {
if getSum(postPaths[i]) &gt; 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 &lt; len(sumList); i++ {
total += sumList[i]
}
return total
}
func getPaths(triangle [][]int) {
prePaths := [][]int{{triangle[0][0]}}
for i := 0; i &lt; len(triangle)-1; i++ {
prePaths = extendPaths(triangle, prePaths)
}
}
func main() {
triangle := [][]int{{3}, {7, 4}, {2, 4, 6}, {8, 5, 9, 3}}
getPaths(triangle)
}

This gives the output in my terminal shown below:

#####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]

Here you can see that for the last 4 times that I set pathA, it is initially set correctly, but then gets overwritten by pathB.

Does anyone have any thoughts on this?

EDIT:

As pointed out by the comments below, what was needed was to make new slices and copy data from the originals. This was done using code from http://blog.golang.org/go-slices-usage-and-internals modified slightly:

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

I also changed the code on the other side, where I created the slices pathA and pathB. This changed to:

for i := 0; i &lt; 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:

It's quite early in the morning here, and I flat out made a mistake in my first edit, I did not fully understand your solution, after a bit of hacking I got there in the end:

This code does not work (pathA gets overwritten):

for i := 0; i &lt; 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)
}

This code also does not work (pathA gets overwritten):

for i := 0; i &lt; 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)
}

However, if I mix the 2 scenarios above into the code below, it works fine (pathA does not get overwritten):

for i := 0; i &lt; 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)
}

So, my solution was to make a copy of the array, and have them both use different ones.

答案1

得分: 6

一个切片基本上是由三个部分组成的结构:

  1. 指向切片中元素数组的指针
  2. 数组的长度(即“容量”)
  3. 实际存储在数组中的元素数量(即“长度”)

当你运行以下代码时:

append(x, element)

它会执行以下操作:

  1. 检查扩展切片是否会超过底层数组的容量。如果是,则分配一个更大的数组,并将现有元素复制到新数组中,并更新容量。
  2. 将新元素(或元素)写入数组的末尾,并更新长度。
  3. 返回新的切片。

在你的代码中,你有以下内容:

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

现在有两种可能性:

  1. len(route) == cap(route),将分配一个新的后备数组,pathApathB 将具有独立的值。
  2. len(route) < cap(route),所以 pathApathB 最终共享同一个后备数组。数组中的最后一个元素将是 nextB,因为该操作是第二次运行的。

似乎在循环的前几次迭代中,第一种情况为真,之后进入了第二种情况。你可以通过手动为其中一个路径创建副本来避免这种情况(使用 make() 分配一个切片,然后使用 copy() 复制旧数据)。

英文:

A slice is basically a structure consisting of 3 things:

  1. A pointer to an array of the elements in the slice
  2. The length of that array (the "capacity")
  3. The number of elements actually stored in the array (the "length")

When you run the following code:

append(x, element)

It does the following:

  1. Check if extending the slice will exceed the capacity of the underlying array. If so, allocate a larger one and copy the existing elements to the new array, and update the capacity.
  2. Write the new element (or elements) to the end of the array and update the length.
  3. Return the new slice.

In your code, you have the following:

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

Now there are two possibilities here:

  1. len(route) == cap(route), and a new backing array will be allocated, with pathA and pathB having independent values.
  2. len(route) &lt; cap(route), so pathA and pathB end up sharing the same backing array. The last element in the array will be nextB, since that operation was run second.

It seems that the first case is true for the first few iterations of your loop, after which you hit the second case. You could avoid this by manually making a copy for one of your paths (allocate a slice with make(), and then use copy() to copy the old data).

huangapple
  • 本文由 发表于 2013年11月29日 10:07:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/20277305.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定