Golang:结构指针方法中的指针可以重新分配给另一个实例吗? [英] Golang: Can the pointer in a struct pointer method be reassigned to another instance?
问题描述
我一直在研究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
)。
与指针一样:当你想改变指针类型变量的值 传递指针是必需的,因为复制是从您传递的所有内容中创建的,并且您只能修改副本。当你传递一个指针时,同样的事情发生了:一个拷贝也是由该指针组成的,但我们并没有修改指针本身,而是指向的值。 b 您想要修改 接收者的类型必须是 要么写一个简单的函数(不是方法),它需要一个 或从函数/方法返回树指针,并让调用者将其指定给作为树指针的变量。 解决你关于返回树指针的问题:这没有什么错。查看内置函数 以下是#1解决方案的样子: 我在上实现了它Go Playground 来证明它有效。 我使用过这种类型: 轻松检查结果,我已经实现了一些产生 这就是如何构建和转换树的: 原始树无需转换): 以及转换后的树(正是您想要的): 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. 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 Code snippet: I've tried other combinations of setting the value or address of tree, and none of them had the intended effect. For example, Additional note: Returning Can the Pointers are values just like let's say When you want to change the value of a variable of type Same goes with pointers: when you want to change the value of a variable of pointer type 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 The receiver's type must be of the form So you have 2 choices: either write a simple function (not method) that takes a 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 Here's how the solution going with #1 would look like: I've implemented it on the Go Playground to prove it works. I've used this type: And to easily check the result, I've implemented some methods to produce a And this is how a tree is constructed and transformed: The original tree (without transformation): And the transformed tree (exactly what you wanted):
这篇关于Golang:结构指针方法中的指针可以重新分配给另一个实例吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋! * int
,你传递一个指向 * int
的指针,它的类型是 ** int
并修改指向的对象: * i =& newvalue
(赋值为 * int $ c
$ b * AvlTree
类型的变量。在Go中,接收器不能是指向指针的指针。 规范:方法声明
T
或 * T
的形式(可能使用圆括号) T
是一个类型名称。由 T
表示的类型称为receiver 基类型; 它不能是指针或接口类型,它必须在方法所在的包中声明。
<所以你有两个选择:
$ b $ ol
** AvlTree
并且你可以传递你的树指针的地址,所以这个函数可以修改树指针(指向的对象)
append()
:它将元素附加到切片并且返回修改的切片。您(调用者)必须将返回的切片分配给切片变量,因为如果附加元素不适合, append()
可以通过分配一个新切片来修改切片原始的(并且自从 append()
接受一个非指针,必须返回修改后的值)。
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
}
键入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>
tree.rotateLeftToRoot()
doesn't result in tree.left
becoming the new tree."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
results in an infinite loop.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
be set to prevLeft
from within the function?int
numbers. The difference is the interpretation of that value: pointers are interpreted as memory addresses, and int
s are interpreted as integer numbers.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
, 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
).*AvlTree
. In Go the receiver cannot be a pointer to pointer. Spec: Method declarations:
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.
**AvlTree
and you can pass the address of your tree pointer, so the function can modify the tree pointer (the pointed object)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).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
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))
}
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)
"t"
"L"
"LL"
<nil>,<nil>
,"LR"
<nil>,<nil>
,"R"
<nil>,<nil>
"L"
"LL"
<nil>,<nil>
,"t"
"LR"
<nil>,<nil>
,"R"
<nil>,<nil>