Golang:结构指针方法中的指针可以重新分配给另一个实例吗? [英] Golang: Can the pointer in a struct pointer method be reassigned to another instance?

查看:222
本文介绍了Golang:结构指针方法中的指针可以重新分配给另一个实例吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



从结构体指针方法中分配主指针似乎在超出范围功能。例如。 tree.rotateLeftToRoot()不会导致 tree.left 成为新树。



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



代码片段:

  // t.rotateLeftToRoot()的图形表示:
// t L
// LR - > LL t
// LL LR LR
func(tree * AvlTree)rotateLeftToRoot(){
if tree == nil {
return
}
prevLeft:= tree.left
如果prevLeft!= nil {
tree.left = prevLeft.right //tree.left传递root右分支
prevLeft.right = tree // tree变成tree.left的右分支
tree.updateHeight()
prevLeft.updateHeight()
tree = prevLeft //所需行为:tree.left变为新树
//实际行为:函数返回时无效果
}
}

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



其他注意事项:返回 tree 并设置tree = tree.rotateLeftToRoot()可以避免这个问题。这很有效,但当调用者真的想调用一个函数来更新树时,混合效果和需要赋值给返回值似乎很肮脏。



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

解决方案

指针的值就像我们说的 int 数字。区别在于对该值的解释:指针被解释为内存地址, int s被解释为整数。



当你想改变 int 类型的变量的值时,你传递一个指向 int 它的类型是 * int ,并且您修改了指向的对象: * i = newvalue (赋值为 int )。



与指针一样:当你想改变指针类型变量的值 * int ,你传递一个指向 * int 的指针,它的类型是 ** int 并修改指向的对象: * i =& newvalue (赋值为 * int

传递指针是必需的,因为复制是从您传递的所有内容中创建的,并且您只能修改副本。当你传递一个指针时,同样的事情发生了:一个拷贝也是由该指针组成的,但我们并没有修改指针本身,而是指向的值。 b
$ b

您想要修改 * AvlTree 类型的变量。在Go中,接收器不能是指向指针的指针。 规范:方法声明


接收者的类型必须是 T * T 的形式(可能使用圆括号) T 是一个类型名称。由 T 表示的类型称为receiver 基类型; 它不能是指针或接口类型,它必须在方法所在的包中声明。



<所以你有两个选择:
$ b $ ol

  • 要么写一个简单的函数(不是方法),它需要一个 ** AvlTree 并且你可以传递你的树指针的地址,所以这个函数可以修改树指针(指向的对象)


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


  • 解决你关于返回树指针的问题:这没有什么错。查看内置函数 append() :它将元素附加到切片并且返回修改的切片。您(调用者)必须将返回的切片分配给切片变量,因为如果附加元素不适合, append()可以通过分配一个新切片来修改切片原始的(并且自从 append()接受一个非指针,必须返回修改后的值)。

    以下是#1解决方案的样子:

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

    我在上实现了它Go Playground 来证明它有效。



    我使用过这种类型:

     键入AvlTree struct {
    value string
    剩下* AvlTree
    正确* AvlTree
    }

    轻松检查结果,我已经实现了一些产生字符串表示的方法:

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

    这就是如何构建和转换树的:

      tree:=& AvlTree {
    value:t,
    left:& AvlTree {
    value:L,$ b剩余$ b:& AvlTree {
    值:LL,
    },
    右:& AvlTree {
    值:LR,
    },
    },
    右:& AvlTree {
    值:R,
    },
    }
    fmt.Println(树)
    rotateLeftToRoot(& tree)
    fmt.Println(tree)

    原始树无需转换):

     t
    L
    LL
    < ;零>,<零>
    ,LR
    < nil>,< nil>

    ,R
    < nil>,< nil>

    以及转换后的树(正是您想要的):

     L
    LL
    < nil>,< nil>
    ,t
    LR
    < nil>,< nil>
    ,R
    < nil>,< nil>


    I've been looking into Golang and have been implementing a few data structures to learn how the language works. I've come across the following issue while writing the code for an AVL tree:

    Assigning the primary pointer from a struct pointer method seems to have no effect outside the scope of the function. E.g. tree.rotateLeftToRoot() doesn't result in tree.left becoming the new tree.

    Question: Is there a way to reassign the pointer in a struct pointer method in Golang, or is this generally discouraged? In the example this would be the "tree = prevLeft" line.

    Code snippet:

    //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
       }
    }
    

    I've tried other combinations of setting the value or address of tree, and none of them had the intended effect. For example, *tree = *prevLeft results in an infinite loop.

    Additional note: Returning tree and setting "tree = tree.rotateLeftToRoot()" avoids the issue. This works, but it seems dirty to be mixing effects and requiring assignment to returned values, when the caller really just wants to be able to call a function to update the tree.

    Can the tree be set to prevLeft from within the function?

    解决方案

    Pointers are values just like let's say int numbers. The difference is the interpretation of that value: pointers are interpreted as memory addresses, and ints are interpreted as integer numbers.

    When you want to change the value of a variable of type int, you pass a pointer to that int which is of type *int, and you modify the pointed object: *i = newvalue (the value assigned is an int).

    Same goes with pointers: when you want to change the value of a variable of pointer type *int, you pass a pointer to that *int which is of type **int and you modify the pointed object: *i = &newvalue (the value assigned is an *int).

    Passing a pointer is required because a copy is made from everything you pass, and you could only modify the copy. When you pass a pointer, the same thing happens: a copy is also made of that pointer, but we're not modifying the pointer itself but the pointed value.

    You want to modify a variable of type *AvlTree. In Go the receiver cannot be a pointer to pointer. Spec: Method declarations:

    The receiver's type must be of the form T or *T(possibly using parentheses) where T is a type name. The type denoted by T is called the receiver base type; it must not be a pointer or interface type and it must be declared in the same package as the method.

    So you have 2 choices:

    1. either write a simple function (not method) that takes a **AvlTree and you can pass the address of your tree pointer, so the function can modify the tree pointer (the pointed object)

    2. or return the tree pointer from your function/method and have the caller assign it to the variable being the tree pointer.

    Addressing your concerns regarding returning the tree pointer: there's nothing wrong with that. Take a look at the builtin function append(): it appends elements to a slice and returns the modified slice. You (the caller) have to assign the returned slice to your slice variable, because append() may modify the slice by allocating a new one if the additional elements do not fit into the original (and since append() takes a non-pointer, the modified value must be returned).

    Here's how the solution going with #1 would look like:

    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
    }
    

    I've implemented it on the Go Playground to prove it works.

    I've used this type:

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

    And to easily check the result, I've implemented some methods to produce a string representation:

    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))
    }
    

    And this is how a tree is constructed and transformed:

    tree := &AvlTree{
        value: "t",
        left: &AvlTree{
            value: "L",
            left: &AvlTree{
                value: "LL",
            },
            right: &AvlTree{
                value: "LR",
            },
        },
        right: &AvlTree{
            value: "R",
        },
    }
    fmt.Println(tree)
    rotateLeftToRoot(&tree)
    fmt.Println(tree)
    

    The original tree (without transformation):

    "t"
        "L"
            "LL"
                <nil>,<nil>
            ,"LR"
                <nil>,<nil>
    
        ,"R"
            <nil>,<nil>
    

    And the transformed tree (exactly what you wanted):

    "L"
        "LL"
            <nil>,<nil>
        ,"t"
            "LR"
                <nil>,<nil>
            ,"R"
                <nil>,<nil>
    

    这篇关于Golang:结构指针方法中的指针可以重新分配给另一个实例吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆