Golang:结构指针方法中的指针可以重新分配给另一个实例吗?

时间:2016-02-16 00:13:28

标签: pointers methods go tree

我一直在研究Golang,并且已经实现了一些数据结构来了解语言的工作原理。在编写AVL树的代码时,我遇到了以下问题:

从结构指针方法中分配主指针似乎在函数范围之外没有任何影响。例如。 tree.rotateLeftToRoot()导致tree.left无法成为新树。

问题:有没有办法在Golang的结构指针方法中重新分配指针,或者这通常是不鼓励的?在示例中,这将是"tree = prevLeft"行。

代码段:

//Graphical representation of t.rotateLeftToRoot():
//      t                  L
//   L     R     ->    LL     t
//LL LR                     LR  R
func (tree *AvlTree) rotateLeftToRoot() {
   if tree == nil {
      return
   }
   prevLeft := tree.left
   if prevLeft != nil {
      tree.left = prevLeft.right //tree.left passed root its right branch
      prevLeft.right = tree      //tree becomes tree.left's right branch
      tree.updateHeight()
      prevLeft.updateHeight()
      tree = prevLeft            //desired behaviour: tree.left becomes the new tree
                                 //actual behaviour: no effect when function returns
   }
}

我尝试过设置树的值或地址的其他组合,但没有一个具有预期的效果。例如,*tree = *prevLeft会导致无限循环。

其他说明:返回tree并设置"tree = tree.rotateLeftToRoot()"可避免此问题。这是有效的,但是当调用者真的只想调用一个函数来更新树时,混合效果并要求赋值给返回值似乎很脏。

可以在函数中将tree设置为prevLeft吗?

1 个答案:

答案 0 :(得分:9)

指针的价值就像我们说的int数字一样。不同之处在于对该值的解释:指针被解释为内存地址,int被解释为整数。

如果要更改int类型变量的值,则将指针传递给类型为int的{​​{1}},并修改指向的对象:{ {1}}(分配的值为*int)。

与指针相同:当您想要更改指针类型*i = newvalue的变量的值时,您将指针传递给类型为int的{​​{1}}的指针并进行修改指向的对象:*int(指定的值为*int)。

传递指针是必需的,因为副本来自您传递的所有内容,并且您只能修改副本。传递指针时,会发生同样的事情:副本也是由该指针构成的,但我们并没有修改指针本身,而是指向值。

您想要修改**int类型的变量。在Go中,接收器不能是指向指针的指针。 Spec: Method declarations:

  

接收者的类型必须是*i = &newvalue*int(可能使用括号),其中*AvlTree是类型名称。 T表示的类型称为接收器基本类型; 它不能是指针或接口类型,必须在与方法相同的包中声明。

所以你有两个选择:

  1. 要么写一个带*T的简单函数(不是方法),你可以传递树指针的地址,这样函数就可以修改树指针(指向对象)

  2. 或从函数/方法返回树指针,并让调用者将其指定给作为树指针的变量。

  3. 解决你关于返回树指针的顾虑:那没有什么不妥。看看内置函数append():它将元素附加到切片返回修改后的切片。您(调用者)必须将返回的切片分配给切片变量,因为T可能会通过分配新切片来修改切片,如果其他元素不适合原始切片(并且T需要一个非指针,必须返回修改后的值。)

    以下##的解决方案如何:

    **AvlTree

    我已经在Go Playground上实施了它,以证明它有效。

    我使用过这种类型:

    append()

    为了便于检查结果,我实施了一些方法来生成append()表示:

    func rotateLeftToRoot(ptree **AvlTree) {
        tree := *ptree
        if tree == nil {
            return
        }
        prevLeft := tree.left
        if prevLeft != nil {
            tree.left = prevLeft.right
            prevLeft.right = tree
            tree = prevLeft
        }
        *ptree = tree
    }
    

    这就是树的构建和转换方式:

    type AvlTree struct {
        value string
        left  *AvlTree
        right *AvlTree
    }
    

    原始树(没有转换):

    string

    转化后的树(正是你想要的):

    func (tree *AvlTree) String() string { return tree.str(1) }
    
    func (tree *AvlTree) str(n int) string {
        if tree == nil {
            return "<nil>"
        }
        return fmt.Sprintf("%q\n%s%v,%v\n%s", tree.value, strings.Repeat("\t", n),
            tree.left.str(n+1), tree.right.str(n+1), strings.Repeat("\t", n-1))
    }