为什么我的Go指针接收器不会引起更新?

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

Why does my Go pointer receiver not cause an update?

问题

我很乐意帮助你解释Go语言中指针接收器的工作原理。

你提供了一个二叉搜索树的示例代码,我会用中文翻译一下:

package main

import "fmt"

type Node struct {
  key         int
  left, right *Node
}

func NewNode(key int) *Node {
  return &Node{key, nil, nil}
}

type BST struct {
  root *Node
}

func NewBinarySearchTree() *BST {
  return &BST{nil}
}

func (t *BST) Insert(key int) {
  if t.root == nil {
    t.root = NewNode(key)
    return
  }
  var node = t.root
  for {
    if key < node.key {
      if node.left == nil {
        node.left = NewNode(key)
        return
      } else {
        node = node.left
      }
    } else {
      if node.right == nil {
        node.right = NewNode(key)
        return
      } else {
        node = node.right
      }
    }
  }
}

func inorder(node *Node) {
  if node == nil {
    return
  }
  inorder(node.left)
  fmt.Print(node.key, " ")
  inorder(node.right)
}

func main() {
  tree := NewBinarySearchTree()
  tree.Insert(3)
  tree.Insert(1)
  tree.Insert(2)
  tree.Insert(4)
  inorder(tree.root) // 1 2 3 4
}

然后,你尝试简化Insert函数,代码如下:

func (t *BST) Insert2(key int) {
  var node *Node
  node = t.root
  for node != nil {
    if key < node.key {
      node = node.left
    } else {
      node = node.right
    }
  }
  node = NewNode(key)
}

然而,用这种方式实现的话,树将不会被更新。你的思路是这样的:

  • 在第一次插入时,根节点将为nil
  • 所以,引用t.root的局部变量node也将为nil
  • 因此,for循环将被跳过。
  • node = NewNode(key)将具有与t.root = NewNode(key)相同的效果。

你的Insert2方法出了什么问题?有没有办法对它进行调整?

英文:

I'd like some help please on how Go pointer receivers work.

I have a contained example below of a binary search tree which hopefully helps me explain.

package main
import &quot;fmt&quot;
type Node struct {
key         int
left, right *Node
}
func NewNode(key int) *Node {
return &amp;Node{key, nil, nil}
}
type BST struct {
root *Node
}
func NewBinarySearchTree() *BST {
return &amp;BST{nil}
}
func (t *BST) Insert(key int) {
if t.root == nil {
t.root = NewNode(key)
return
}
var node = t.root
for {
if key &lt; node.key {
if node.left == nil {
node.left = NewNode(key)
return
} else {
node = node.left
}
} else {
if node.right == nil {
node.right = NewNode(key)
return
} else {
node = node.right
}
}
}
}
func inorder(node *Node) {
if node == nil {
return
}
inorder(node.left)
fmt.Print(node.key, &quot; &quot;)
inorder(node.right)
}
func main() {
tree := NewBinarySearchTree()
tree.Insert(3)
tree.Insert(1)
tree.Insert(2)
tree.Insert(4)
inorder(tree.root) // 1 2 3 4
}

After I wrote this, however, I thought I could simplify my insert function as follows:

func (t *BST) Insert2(key int) {
var node *Node
node = t.root
for node != nil {
if key &lt; node.key {
node = node.left
} else {
node = node.right
}
}
node = NewNode(key)
}

However, doing it this way the tree is never updated.
My thinking was...

  • on the first insert the root node will be nil.
  • so the local variable node which references t.root will also be nil
  • the for loop will therefore be skipped.
  • node = NewNode(key) will have the same effect as t.root =
    NewNode(key)

Where does my Insert2 method go wrong? Is there a way it can be tweaked?

答案1

得分: 3

node = NewNode(key)

这行代码不会改变树的结构。这行代码只是改变了局部变量 node 的指向;在这行代码之后,node 指向一个不同的节点,但它之前指向的节点对象并没有受到影响。要将节点插入到树中,你需要将其赋值给 t.rootnode.left 或者 node.right

英文:
node = NewNode(key)

That line doesn't change the tree. That line changes the local variable node; after this line, node points to a different Node, but the object it used to point to is unaffected. To insert into the tree, you have to assign to t.root, node.left, or node.right.

答案2

得分: 3

你似乎对指针的使用有些混淆。

当你执行node = t.root时,你只是让node指向root指向的内容。

之后,当你执行node = NewNode(key)时,你让node指向一个新创建的项,这不是你想要的;你想要的是让t.root指向这个新项。

由于你打算修改类型为*Noderootleftright)的变量,我们需要一个指向它们的指针,即类型为**Node的变量,多了一层间接引用。

你可以先让node指向t.root的地址,node := &t.root,然后继续你的循环。

你可以尝试以下代码:

func (t *BST) Insert3(key int) {
    node := &t.root
    for *node != nil {
        if key < (*node).key {
            node = &(*node).left
        } else {
            node = &(*node).right
        }
    }
    *node = NewNode(key)
}

请注意,在循环中,我们使用间接操作符*来访问引用的数据,包括检查循环中的地址和键。

在函数的最后,*node = NewNode(key)实现了你最初的意图;你将新创建的项分配给了根、左子树或右子树指针。

英文:

You seem to be confusing the usage of pointers.

When you do node = t.root, you merely makes node point to whatever t.root points to.

Later on, when you do node = NewNode(key), you make node points to a newly created item, which is not what you wanted; you want to make t.root point to that new item instead.

Since you intend to modify variables which are of type *Node (root, left and right), we need a pointer to them, so a variable of type **Node, with one more level of indirection.

You can start by making node point to the address of t.root, node := &amp;t.root, then you proceed to your loop.

You can try something like the following:

func (t *BST) Insert3(key int) {
node := &amp;t.root
for *node != nil {
if key &lt; (*node).key {
node = &amp;(*node).left
} else {
node = &amp;(*node).right
}
}
*node = NewNode(key)
}

Pay attention that we use the indirection operator * to access the <i>referenced data</i>, when checking the address on the loop, and also the key.

In the end of the function, *node = NewNode(key) does what you intended to do originally; you are assigning the newly created item to the root, left or right pointers.

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

发表评论

匿名网友

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

确定