猿问

在 Go 中分配切片与重新声明切片

我试图使用切片作为队列数据结构,我想出了这个实现,这导致了一个无限循环。这是因为切片不会使用子切片 进行更新。queuequeue[1:]


func badQueue() {

    queue := []int{0,1,2,3,4,5}

    for len(queue) > 0 {

        current, queue := queue[0], queue[1:]

        fmt.Println(current, queue)

    }

}

0 [1 2 3 4 5]

0 [1 2 3 4 5]

0 [1 2 3 4 5]

0 [1 2 3 4 5]

0 [1 2 3 4 5]

...

我已经发现这个问题与我正在重新声明和(with)而不是分配值的事实有关,这解决了这个问题:currentqueue:=


func goodQueue() {

    queue := []int{0,1,2,3,4,5}

    var current int

    for len(queue) > 0 {

        current, queue = queue[0], queue[1:]

        fmt.Println(current, queue)

    }

}

0 [1 2 3 4 5]

1 [2 3 4 5]

2 [3 4 5]

3 [4 5]

4 [5]

5 []

我知道导致问题的原因,但我不完全理解为什么在这种情况下的重新声明操作与分配的工作方式不同。为什么不使用队列 () 的子切片重新声明队列?queue[1:]


谢谢!


长风秋雁
浏览 98回答 3
3回答

牛魔王的故事

因为可以有多个具有相同名称的变量,只要它们具有不同的作用域即可。内部作用域中的变量将遮蔽外部作用域中的变量。因此,如果我们分解您的示例func badQueue() {    // queue from outer scope, lets call it A    queue := []int{0,1,2,3,4,5}    // the only visible queue here is A, so len(queue) will always refer to A    for len(queue) > 0 {        // same thing here, the only visible queue is A, so queue[0] and queue[1:]        // both refer to A        // We are also declaring new variables, queue and current        // This queue is now shadowing the outer queue, let's call this one B        current, queue := queue[0], queue[1:]        // Here queue will refer to B        fmt.Println(current, queue)        // When the current iteration of the loop ends current and queue B is destroyed        // because they go out of scope and the loop start over with A unchanged    }}

SMILET

短声明情况下的变量的作用域仅限定为循环的主体,即每次新迭代都销毁和创建。如短变量声明中引用queue与常规变量声明不同,短变量声明可以重新声明变量,前提是它们最初是在同一块(或者如果块是函数体,则参数列表)中以相同类型声明的,并且至少有一个非空白变量是新的。因此,重新声明只能出现在多变量短声明中。重新声明不会引入新变量;它只是为原始值分配一个新值。

qq_笑_17

要了解它在 Go 中的工作原理,需要两点:范围Go 使用块作用域,每个大括号对将创建一个新作用域,如果内部作用域中的标识符具有相同的声明名称,则它们将隐藏外部作用域中的标识符。 func main() {     var name string = "Golang"     fmt.Printf("Outer Scope: %s\n", name) // Print "Golang"     {         var name string = "Java"         fmt.Printf("Inner Scope: %s\n", name)   // Print "Java"     }        fmt.Printf("Outer Scope: %s\n", name)  // Print "Golang" again  }短变量声明Operator是一个复合操作,它将在一个语句中做几件事:声明,类型推断和赋值,基本上你可以把它当作一个语法糖。以下 S1 和 S2 的代码示例是等效的::=func main() {    // S1    var name string = "Golang"     // S2    name := "Golang"}考虑到以上两点,您的代码在翻译后将如下所示:func badQueue() {    queue := []int{0,1,2,3,4,5}    for len(queue) > 0 {        var current int        var queue []int        current, queue = queue[0], queue[1:]        fmt.Println(current, queue)    }}很明显,外部不受影响内部循环。queuefor顺便说一句,对于左侧的每个变量,编译器将查找当前块范围以尝试解析标识符,如果之前已经声明过,编译器将重用它而不是创建一个新的。但是,如果之前声明了所有 lhs 变量,编译器将报告一个错误,并显示消息“:=的左侧没有新变量”。请查看以下代码::=func main() {    var name string    name, age := "Golang", 10 // reuse the above 'name' and create a new variable 'age'    var tom, jerry string    tom, jerry := "Tom", "Jerry" // no new variables on left side of :=}新的编译器实现在这里,适合那些对细节感兴趣的人。
随时随地看视频慕课网APP

相关分类

Go
我要回答