“按值传递切片”和“底层数组”在Golang中的行为是怎样的?

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

How 'pass by value slice' and 'the underlying array' behaves in Golang?

问题

我正在尝试在GO中实现深度优先搜索,并且我遇到了以下问题。

在GO中,当我们将一个切片传递给一个函数时,它应该创建一个新的切片头部,因为GO是按值传递的语言,但同时新创建的切片应该指向与之前的切片指向的相同的底层数组。

但是在我的深度优先搜索函数中,传递的切片似乎没有指向相同的数组。

如下所示,我在递归调用深度优先遍历函数时使用节点和切片作为参数,其中切片存储每个访问节点的值。在遍历整个树之后,切片应该包含所有的节点,但是切片是空的。

func (b *BinarySearchTree) DFSInOrder(node *Node, list []int) {

	if node.Left != nil {
		b.DFSInOrder(node.Left, list)
	}

	list = append(list, node.Value)

	if node.Right != nil {
		b.DFSInOrder(node.Right, list)
	}
}

func main() {
    //...
	//BinarySearchTree creation code
    //...
	
	var list []int
	b.DFSInOrder(b.Root, list)
	fmt.Println()
	fmt.Println("List after traversing:", list)
}

理想情况下,在每次递归调用中,即使它创建了新的切片,它也应该更新相同的底层数组,最终原始切片应该包含所有的节点,但是它是空的。

有人可以解释一下为什么会发生这种情况吗?

请在此处找到更多详细代码:https://play.golang.com/p/PCrADg3zYV8
请检查第51行的函数。

注意:如果我传递切片指针(list *[]int),那么它就可以正常工作。

英文:

I am trying to implement a depth first search in GO and I am facing below issue.

In GO whenever we pass a slice to a function it should create a new slice header as GO is pass by value language but at the same time newly created slice should point to the same underlying array to which previous slice was pointing.

But Looks like in my depth first search function, passed slice is not pointing to the same array.

As we can see in below mentioned code I am calling depth first traverse function recursively with node and slice as parameters where slice stores the value for each visited node. After traversing entire tree I should get all the nodes in the slice but the slice was empty.

func (b *BinarySearchTree) DFSInOrder(node *Node, list []int) {

	if node.Left != nil {
		b.DFSInOrder(node.Left, list)
	}

	list = append(list, node.Value)

	if node.Right != nil {
		b.DFSInOrder(node.Right, list)
	}
}

func main() {
    //...
	//BinarySearchTree creation code
    //...
	
	var list []int
	b.DFSInOrder(b.Root, list)
	fmt.Println()
	fmt.Println("List after traversing:", list)
}

Ideally in each recursive call even if it is creating new slice it should update the same underlying array and finally original slice should have all the nodes but it was empty.

Can some one please explain why this is happening?

Please find full code for more details here: https://play.golang.com/p/PCrADg3zYV8 <br>
Check function at line 51.

Note: If I pass slice pointer(list *[]int) then it works fine.

答案1

得分: 1

请看这个示例 go playgroundappend 函数会分配一个新的切片,并将新的切片保存到值中,而不是引用。你是正确的,你不需要通过引用传递切片来修改底层元素,这就是我示例中的 editSlice 函数所做的。

package main

import (
	"fmt"
)

func main() {
	slice := []int{10, 20}
	appendSlice(slice)
	fmt.Println("slice after append ", slice)

	editSlice(slice)
	fmt.Println("slice after edit ", slice)
}

func appendSlice(s []int) {
	s = append(s, s[0])
	fmt.Println("the appended slice ", s)
}

func editSlice(s []int) {
	s[0] = s[0] * 2
}

如果你仍然想在你的代码中使用 append 语法,我已经更新了代码以通过引用传递并修改切片(go playground)。

英文:

See this example go playground. append is allocating a new slice, and saving the new slice to the value, not the reference. You are correct, you don't need to pass a slice by reference to modify the underlying elements which is what the editSlice function does in my example.

package main

import (
	&quot;fmt&quot;
)

func main() {
	slice := []int{10, 20}
	appendSlice(slice)
	fmt.Println(&quot;slice after append &quot;, slice)

	editSlice(slice)
	fmt.Println(&quot;slice after edit &quot;, slice)
}

func appendSlice(s []int) {
	s = append(s, s[0])
	fmt.Println(&quot;the appended slice &quot;, s)
}

func editSlice(s []int) {
	s[0] = s[0] * 2
}

If you still want to use the append syntax in your code, I updated it to pass and modify the slice by reference (go playground).

huangapple
  • 本文由 发表于 2021年7月7日 22:41:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/68288151.html
匿名

发表评论

匿名网友

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

确定