英文:
Why does accessing a nil pointer in Go cause an Error in Inorder traversal
问题
我已经用Python编写了一个正常工作的中序遍历函数。
def inOrderTraverse(tree, array):
if tree is None:
return None
inOrderTraverse(tree.left, array)
array.append(tree.value)
inOrderTraverse(tree.right, array)
return array
然而,当我尝试使用相同的逻辑在Go语言中实现时,它不起作用。
type BST struct {
Value int
Left *BST
Right *BST
}
func (tree *BST) InOrderTraverse(array []int) []int {
if tree == nil {
return nil
}
(tree.Left).InOrderTraverse(array)
array = append(array, tree.Value)
(tree.Right).InOrderTraverse(array)
return array
}
为了修复这个问题,我编写了一些if语句,以防止在tree.Left为nil时调用函数。然而,我仍然困惑于为什么我的原始代码不起作用。
func (tree *BST) InOrderTraverse(array []int) []int {
if tree.Left != nil {
array = tree.Left.InOrderTraverse(array)
}
array = append(array, tree.Value)
if tree.Right != nil {
array = tree.Right.InOrderTraverse(array)
}
return array
}
总结一下,我试图使用相同的思路在Go语言中编写中序遍历函数,但它不起作用。我找到了解决方法(第三个代码块),但我仍然不明白为什么第二个代码块不起作用。似乎在空指针上调用函数会导致错误。
英文:
I have written inorder traversal in python which works fine.
def inOrderTraverse(tree, array):
if tree is None:
return None
inOrderTraverse(tree.left,array)
array.append(tree.value)
inOrderTraverse(tree.right,array)
return array
However when I try to apply the same logic with GOlang, it does not work.
type BST struct {
Value int
Left *BST
Right *BST
}
func (tree *BST) InOrderTraverse(array []int) []int {
if tree == nil {
return nil
}
(tree.Left).InOrderTraverse(array)
array = append(array,tree.Value)
(tree.Right).InOrderTraverse(array)
return array
}
In order to fix this I write if statements to prevent the functions from being called if tree.Left is nil. However I am still confused why my original code does not work.
func (tree *BST) InOrderTraverse(array []int) []int {
if tree.Left != nil {
array = tree.Left.InOrderTraverse(array)
}
array = append(array,tree.Value)
if tree.Right != nil {
array = tree.Right.InOrderTraverse(array)
}
return array
}
To summarize, I am trying to write inorder traversal in Go using the same reasoning from python and it is not working. I figured out how to get it to work(the third code block), however I still do not understand why the second code block does not work. It seems like calling the function on the Nil pointer causes an error
答案1
得分: 3
几件事情:
在你的第二个代码块中,它不起作用的原因是因为结果被丢弃了:
// (tree.Left).InOrderTraverse(array) // 忽略了返回结果
array = (tree.Left).InOrderTraverse(array) // 这样捕获它
Python
使用按引用传递;Go
使用按值传递。因此,当在 Go 中修改像 slices
这样的东西时,可能会变得棘手。
由于你已经返回了遍历的结果,传入 array
实际上是多余的。
将所有这些整合在一起,修复左右遍历,并使用返回变量 result
:
func (tree *BST) InOrderTraverse() (result []int) {
if tree == nil {
return // 隐式返回空的 `result`
}
result = append(result, (tree.Left).InOrderTraverse()...)
result = append(result, tree.Value)
result = append(result, (tree.Right).InOrderTraverse()...)
return // 隐式返回 `result`
}
工作示例:https://play.golang.org/p/AUvgZTABiU9
编辑
一个 Go
的 "按引用传递" 实现 - 在调用之间传递 results
切片 - 看起来与你的 python
实现非常相似:
func (tree *BST) InOrderTraverse(result *[]int) {
if tree == nil {
return
}
(tree.Left).InOrderTraverse(result)
*result = append(*result, tree.Value)
(tree.Right).InOrderTraverse(result)
}
https://play.golang.org/p/Pp9-4-y-lmE
注意:这里需要 *[]int
(即指向 []int
的指针),因为在递归过程中切片的容量会发生变化。切片实际上只是一个带有后备数组的标头 - 因此,虽然函数可以更改切片的元素(即使按值复制),但它不能使切片收缩或增长(正如在这里的情况)。传递指针允许将切片重新分配给一个可能更大的后备数组。
英文:
A couple of things:
In your 2nd block of code, the reason it does not work is because the result is discarded:
// (tree.Left).InOrderTraverse(array) // the return result is ignored
array = (tree.Left).InOrderTraverse(array) // capture it like so
Python
uses pass by-reference; Go
passes by value. As such it can get tricky especially when modifying things like slices
in Go.
Since you're already returning the results of the traversal, passing in the array
is actually redundant.
Pulling this all together to fix both left & right traversal - and using a return variable result
:
func (tree *BST) InOrderTraverse() (result []int) {
if tree == nil {
return // implicitly returns empty `result`
}
result = append(result, (tree.Left).InOrderTraverse()...)
result = append(result, tree.Value)
result = append(result, (tree.Right).InOrderTraverse()...)
return // implicitly returns `result`
}
Working example: https://play.golang.org/p/AUvgZTABiU9
EDIT
A Go
"pass by reference" implementation - where the results
slice is passed between calls - looks very similar to your python
implementation:
func (tree *BST) InOrderTraverse(result *[]int) {
if tree == nil {
return
}
(tree.Left).InOrderTraverse(result)
*result = append(*result, tree.Value)
(tree.Right).InOrderTraverse(result)
}
https://play.golang.org/p/Pp9-4-y-lmE
Note: *[]int
(i.e. a pointer to []int
) is needed here, as the slice capacity will change during the recursion. A slice is really just a header with a backing array - so while a function can change a slice's elements - even when copied by value - it cannot make the slice shrink or grow (as is the case here). Passing a pointer allows the slice to be reassigned to a potentially larger backing array.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论