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

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

问题描述

我一直在研究Golang,并且已经实现了一些数据结构来学习该语言的工作方式.在为AVL树编写代码时遇到了以下问题:

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:

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

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.

问题:有没有一种方法可以在Golang的struct指针方法中重新分配指针,或者通常不建议这样做?在示例中,这将是"tree = prevLeft"行.

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.

代码段:

//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会导致无限循环.

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.

附加说明:返回tree并设置"tree = tree.rotateLeftToRoot()"可以避免此问题.这可行,但是当调用者确实只想能够调用一个函数来更新树时,混合效果并要求分配返回值似乎很肮脏.

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.

能否在功能内将tree设置为prevLeft?

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

推荐答案

指针是类似于int数字的值.区别在于该值的解释:指针被解释为内存地址,而int被解释为整数.

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.

当您想更改类型int的变量的值时,将指针传递到类型为*intint,然后修改指向的对象:*i = newvalue(分配的值是int).

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

指针也是如此:当您要更改指针类型*int的变量的值时,您将指针传递给类型为**int*int,然后修改指向的对象:(分配的值是*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.

您要修改类型为*AvlTree的变量.在Go语言中,接收者不能是指向指针的指针. 规范:方法声明:

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

接收者的类型必须为T*T的形式(可能使用括号),其中T是类型名称. T表示的类型称为接收方基本类型它不能是指针或接口类型,并且必须在与方法相同的程序包中声明.

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.

因此,您有2个选择:

  1. 要么编写一个简单的函数(而不是方法),该函数需要一个**AvlTree,您就可以传递树指针的地址,因此该函数可以修改树指针(指向的对象)

  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)

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

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

解决您对返回树形指针的担忧:这没有错.看看内置函数 append() :它将元素附加到切片并且返回修改后的切片.您(调用者)必须将返回的切片分配给您的slice变量,因为append()可能会通过在其他元素不适合原始元素的情况下分配新的切片来修改切片(并​​且append()采用非指针) ,则必须返回修改后的值.)

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

以下是与#1搭配使用的解决方案的样子:

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.

我使用了这种类型:

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

为了轻松检查结果,我实现了一些方法来生成string表示形式:

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>

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

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