使用切片嵌套切片策略的多维数组在 Golang 中产生奇怪的输出问题

3

我正在使用以下简单的代码来处理Golang中的二维数组,其中APPEND函数结果是重复值而不是追加。

package main

import "fmt"

func main() {
    var n int
    fmt.Scanf("%d", &n)
    array := [][]int{}
    row := make([]int, n)
    for _, _ = range row {
        for j, _ := range row {
            fmt.Scanf("%d", &row[j])
        }
        fmt.Println("Printing current Row", row)
        array = append(array, row)
        fmt.Println("Printing curent Array", array)
    }
    fmt.Println("Final Array", array)
}

但奇怪的是这些事情并没有出乎意料。如果假设我想让这件事发生(输入)。
2
1 2
3 4

当我运行这个程序时,会得到以下结果。
2     //Dimension for matrix
1     //Iteration one begins
2
Printing current Row [1 2]
Printing curent Array [[1 2]]
3     //Iteration two begins
4
Printing current Row [3 4]
Printing curent Array [[3 4] [3 4]]
Final Array [[3 4] [3 4]]

我不明白为什么使用APPEND函数会导致重复的条目。 因此,想知道如何纠正这个问题以及其中的概念


你确定你复制代码正确吗?当我将你的程序复制到 https://play.golang.org/p/mJeZsXlP7r 并运行它时,它只会产生输出 'Final Array []'(试一下) - dmitris
@dmitris 你确定 Golang Playground 允许我们在运行时提供输入吗? - giri-sh
没关系,你是对的——Playground 中没有 stdin!:) https://golang.org/pkg/fmt/#Scanf——“Scanf 从标准输入中扫描文本,并根据格式将连续的以空格分隔的值存储到连续的参数中。” - dmitris
2个回答

3
这种行为发生是因为您没有向数组中添加新的 row 切片。每次迭代只是将值读入相同的 row 切片,覆盖了先前的值。请尝试以下操作:
func main() {
    var n int
    fmt.Scanf("%d", &n)
    array := [][]int{}

    for i := 0; i < n; i++ {
        row := make([]int, n) // create a new slice to add next row values
        for j, _ := range row {
            fmt.Scanf("%d", &row[j])
        }
        fmt.Println("Printing current Row", row)
        array = append(array, row)
        fmt.Println("Printing curent Array", array)
    }
    fmt.Println("Final Array", array)
}

真的想知道为什么append函数既追加又替换现有数据。 - Abhishek Soni

1

为每次第一个循环迭代使用新的切片,并检查错误

您将新数据读入旧的相同切片中,这就是为什么会出现重复数据的原因。

如果您想要使用append,请首先创建长度为且容量为n的数组:
array := make([][]int, 0, n)
然后将您的第一个循环更改为:for i := 0; i < n; i++ {
并在此循环内部创建新的切片:row := make([]int, n)

2种方式:
使用append(不带fmt.Scan错误检查):

package main

import "fmt"

func main() {
    n := 0
    fmt.Scan(&n)
    slice := make([][]int, 0, n)
    for i := 0; i < n; i++ {
        row := make([]int, n)
        for j, _ := range row {
            fmt.Scan(&row[j])
        }
        slice = append(slice, row)
    }
    fmt.Println(slice)
}

不使用append,通过错误检查读取到s[i][j]

package main

import "fmt"

func main() {
    var n int
    if m, err := fmt.Scan(&n); m != 1 {
        panic(err)
    }
    s := make([][]int, n)
    for i := 0; i < n; i++ {
        s[i] = make([]int, n)
        for j := 0; j < n; j++ {
            if m, err := fmt.Scan(&s[i][j]); m != 1 {
                panic(err)
            }
        }
    }
    fmt.Println(s)
}

输入:


2
1 2
3 4

输出:
[[1 2] [3 4]]

我认为这个测试样本代码已经足够清晰地展示了切片嵌套的工作原理(附有注释输出):

package main

import "fmt"

func main() {
    s1 := [][]int{}
    s2 := []int{1, 2}
    s1 = append(s1, s2)
    fmt.Println(s1) // [[1 2]]

    s2[0], s2[1] = 3, 4
    fmt.Println(s1) // [[3 4]]

    s1 = append(s1, s2)

    fmt.Println(s1) // [[3 4] [3 4]]

    s2[0], s2[1] = 30, 40
    fmt.Println(s1) // [[30 40] [30 40]]

    fmt.Println(len(s2), cap(s2)) // 2 2

    s3 := [][]int{
        []int{1, 2},
        []int{3, 4},
        s2,
        s2,
    }
    fmt.Println(s3) // [[1 2] [3 4] [30 40] [30 40]]
    s2[0] = 100
    fmt.Println(s3) // [[1 2] [3 4] [100 40] [100 40]]
}

你创建了一个名为s1的切片嵌套切片,代码如下:
array [0] = row[0], row[1]
array [1] = row[0], row[1]

当你改变row时,它会被看到两次。

谢谢,但我想知道为什么“append”行为如此奇怪,它既附加新行又更新其他行。 - Abhishek Soni
@AbhishekSoni:问题不在于追加,而是您通过fmt.Scanf("%d", &row[j])将数据读入同一内存中。如需追加,请参见:https://golang.org/ref/spec#Appending_and_copying_slices - user6169399
我不明白,因为我认为在第一次迭代中我将一个切片附加到数组中,然后我又将相同的切片与更改后的值再次推入数组中, 现在在下一次附加期间,如何才能获得所有附加值的相同引用。 - Abhishek Soni
@AbhishekSoni:请查看新编辑,希望这可以帮到你。 - user6169399
非常感谢,这很有帮助。我学到的是append()函数基于引用,这个模型与C++或Java完全不同,在将值推送到数组后,您可以使用相同的引用变量。 - Abhishek Soni

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接