Go - recursive struct with array and losing values - go

I created an Element struct that has a parent and children, created a helper func called SubElement, and a String method that iterates through all children for print:
package main
import "fmt"
type Element struct {
parent *Element
children []Element
tag string
}
func SubElement(parent *Element, tag string) Element {
el := Element{}
el.parent = parent
el.tag = tag
parent.children = append(parent.children, el)
return el
}
func (el Element) String() string {
s := "<" + el.tag + ">"
for _, child := range el.children {
s += child.String()
}
s += "</" + el.tag + ">"
return s
}
func main() {
root := Element{}
root.tag = "root"
a := SubElement(&root, "a")
b := SubElement(&a, "b")
SubElement(&b, "c")
fmt.Println(root) // prints: <root><a></a></root>
fmt.Println(a) // prints: <a><b></b></a>
// and so on
}
The problem I'm experiencing is, only the first tier of children are available from the root node I choose to print. I'm sure it's related to the use of append on parent.children, but lack the understanding of how to resolve this correctly.
To work around the issue, I changed children to map[int]Element. Then in my SubElement func, I "append" with parent.children[len(parent.children)] = el. Then to iterate in the correct order, the String method for-loop is for i:= 0; i < len(el.children); i++, accessing el.children[i].
Still, I'd like to know how to do this correctly with an array. Thanks

An answer to explain why the []Element version didn't work.
Structs are copied as values. In SubElement, you create one Element struct, then when you append it, that actually appends a whole new copy of the struct. When you return el and assign it to a, that makes yet another copy. The address of a is not the address of the Element that was appended.
So, you could get tricky and take the address of the element that's actually in the slice, and that might even look like it's working in a test case, but there's a problem with retaining one these pointers such as you do when you store it back in Element.parent. The problem is that a subsequent append can reallocate the slice, and now your retained pointer points into some orphaned memory rather than the currently valid slice.
If the []*Element version solved some other issues, it's likely that they were issues of storing pointers that were subseqently orphaned.
It is possible to implement a tree with slices of structs, but it takes a clear understanding that retaining pointers into slices is usually a mistake. Since storing the parent pointer is not safe, it's best to just remove it from the struct.
package main
import "fmt"
func main() {
tree := Element{tag: "head"}
tree.SubElement("tier-1")
tree.children[0].SubElement("tier-2")
tree.children[0].SubElement("tier-2")
tree.SubElement("tier-1")
tree.children[1].SubElement("tier-2")
fmt.Println(tree)
}
type Element struct {
children []Element
tag string
}
func (parent *Element) SubElement(tag string) {
parent.children = append(parent.children, Element{tag: tag})
}
func (el Element) String() string {
s := "<" + el.tag + ">"
for _, child := range el.children {
s += child.String()
}
s += "</" + el.tag + ">"
return s
}
This code, at least, works; but if you had other code that was working with pointers, or that used the parent pointer, it would have to be rethought.

The first clue is that SubElement doesn't compile as you have it (edit: had it) there (originally.) You could experiment with making it work, but I recommend you change the Element.children to be []*Element rather than []Element. Here's a working example:
package main
import "fmt"
func main() {
tree := &Element{tag: "head"}
t1 := SubElement(tree, "tier-1")
SubElement(t1, "tier-2")
SubElement(t1, "tier-2")
t1 = SubElement(tree, "tier-1")
SubElement(t1, "tier-2")
fmt.Println(tree)
}
type Element struct {
parent *Element
children []*Element
tag string
}
func SubElement(parent *Element, tag string) *Element {
el := &Element{parent: parent, tag: tag}
parent.children = append(parent.children, el)
return el
}
func (el *Element) String() string {
s := "<" + el.tag + ">"
for _, child := range el.children {
s += child.String()
}
s += "</" + el.tag + ">"
return s
}
Output:
<head><tier-1><tier-2></tier-2><tier-2></tier-2></tier-1><tier-1><tier-2></tier-2></tier-1></head>

Related

Reference values not being updated [duplicate]

This question already has an answer here:
Can the pointer in a struct pointer method be reassigned to another instance?
(1 answer)
Closed 4 months ago.
I am new to the Golang world & trying to implement a BST in Golang. But while inserting/updating pointers values are not getting updated/inserted. For example in the following code the output is:
3
[]
my code:
package main
import "fmt"
var inOrderTrace []int = []int{}
type node struct {
value int
leftChild *node
rightChild *node
}
type tree struct {
root *node
len int
}
func (myTree tree) inOrderTraverse(node *node) {
if node == nil {
return
}
myTree.inOrderTraverse(node.leftChild)
inOrderTrace = append(inOrderTrace, node.value)
myTree.inOrderTraverse(node.rightChild)
}
func (myTree *tree) insertNode(nodeToManipulate *node, toInsert int) {
if nodeToManipulate == nil {
nodeToManipulate = &node{toInsert, nil, nil}
myTree.len++
return
}
if nodeToManipulate.value > toInsert {
myTree.insertNode(nodeToManipulate.leftChild, toInsert)
} else {
myTree.insertNode(nodeToManipulate.rightChild, toInsert)
}
}
func main() {
myTree := &tree{nil, 0}
var elements []int = []int{1, 0, 2}
for _, element := range elements {
myTree.insertNode(myTree.root, element)
}
myTree.inOrderTraverse(myTree.root)
fmt.Println(myTree.len)
fmt.Println(inOrderTrace)
}
I am expecting the inserted values to be printed.Thanks.
insertNode() has a parameter of pointer type (nodeToManipulate *node). Inside insertNode():
nodeToManipulate = &node{toInsert, nil, nil}
This line will just assign a pointer to the parameter, a local variable. Calling this method from main(), and passing myTree.root, the myTree.root will never be modified, as written above, only the function parameter (which is a copy, a local variable). This means your tree never gets built, the root of tree never gets modified.
To modify something, you have to pass a pointer to it, and modify the pointed value.
For example:
func (myTree *tree) insertNode(pnodeToManipulate **node, toInsert int) {
if *pnodeToManipulate == nil {
*pnodeToManipulate = &node{toInsert, nil, nil}
myTree.len++
return
}
nodeToManipulate := *pnodeToManipulate
if nodeToManipulate.value > toInsert {
myTree.insertNode(&nodeToManipulate.leftChild, toInsert)
} else {
myTree.insertNode(&nodeToManipulate.rightChild, toInsert)
}
}
func main() {
myTree := &tree{nil, 0}
var elements []int = []int{1, 0, 2}
for _, element := range elements {
myTree.insertNode(&myTree.root, element)
}
myTree.inOrderTraverse(myTree.root)
fmt.Println(myTree.len)
fmt.Println(inOrderTrace)
}
With this change output will be (try it on the Go Playground):
3
[0 1 2]
If you don't like double pointers (**), another option is to return the new value and assign it at the caller.
See related / possible duplicates:
Can the pointer in a struct pointer method be reassigned to another instance?
How to modify the value of a simple type through pointer receiver method in Go?
What use case does pointers to pointer (eg **int) have?

Modify slice element using pointer

I'm currently trying to modify an element of a slice by passing a pointer to a function. Outside of the function the element is not modified.
Is there a way to modify the element without passing the slice itself along with the index of the desired element to alter?
package main
import (
"fmt"
)
type Item struct {
Value int
}
func alter(t *Item) {
(*t).Value = 100
}
func main() {
items := []Item{Item{0}, Item{1}}
for _, item := range items {
alter(&item)
}
fmt.Println(items) // Output is still [{0} {1}]
}
Golang Playground
for i := range items {
alter(&items[i])
}
Or
items := []*Item{{0}, {1}}
for _, item := range items {
alter(item)
}
The reason your version doesn't work is because the iteration variable item holds a copy of the element inside the slice, which means that what you're modifying is the copy and not the original. You can see that they are separate objects in memory if you run this: https://play.golang.org/p/vr9CfX0WQcB
Refer: https://tour.golang.org/moretypes/16
The range form of the for loop iterates over a slice or map.
When ranging over a slice, two values are returned for each iteration. The first is the index, and the second is a copy of the element at that index.
So,
for i, x := range arr {
// x is copy for arr[i]
}
Hence, we will directly used arr[i] and pass the address of the same to the alter function so that it could be modified.
Sample code:
package main
import "fmt"
type Item struct {
Value int
}
func alter(t *Item) {
(*t).Value = 100
}
func main() {
items := []Item{{0}, {1}}
for i := range items {
alter(&items[i])
}
fmt.Println(items)
}
[Playground]

How to pass slice of struct as pointer to a function and modify it?

I have a slice of struct []student, and I want to modify its content with function.
type student struct {
name string
age int
}
students := []student{
{"Doraemon", 30},
{"King Kong", 25},
}
Thus, I decided to pass it as a pointer. May I know how to pass the slice as a reference to a function?
func addAge (s *[]student) error { //this code has error
//everyone add 2 years old
for i, e := range *s {
s[i].age = s[i].age + 2
}
//make the first student much older
s[0].age = s[0].age + 5
return nil
}
I keep playing with Go Playground, but it gives many complains, such as
cannot range over s (type *[]student)
invalid operation: s[i] (type *[]student does not support indexing)
invalid indirect of s
...
How to precisely pass the reference of a slice of struct to a function? How to range the slice of struct? And how to change the value of the struct (modify the same struct in THE slice)?
I keep getting error while playing with s *[]student, range *s, s []student, s *[]*student ... so hard to get it correct...
sorry for my NEWBIE question, still learning GO... trying hard
Slices are passed by reference, so as long as you are modifying the existing slice content you should not explicitly pass a pointer.
package main
import (
"fmt"
)
type student struct {
name string
age int
}
func main() {
students := []student{
{"Doraemon", 30},
{"King Kong", 25},
}
err := addAge (students)
fmt.Println(students)
if err != nil {
fmt.Println("error")
}
}
func addAge (s []student) error {
for i, _ := range s {
s[i].age = 3
}
return nil
}
Now, for your addAdditinalStudent function you should actually use the append function. Plus, have in mind
..., since the slice header is always updated by a call to
append, you need to save the returned slice after the call. In fact,
the compiler won't let you call append without saving the result.
Slices#append
// add student
students = append(students, student{"Test", 33})
Go Playground
in Go you can pass items by value ([]student) or by reference ([]*student). When you want to operate on the values of a struct{} you should pass it to a function with its reference (the pointer).
So you can do something like this:
type student struct {
name string
age int
}
func addTwoYearsToAll(students []*student){
for _, s := range students {
s.age += 2
}
}
This way you're working with the same exact items you build when appending to the slice. Playground example.
Also take a look at Are Golang function parameter passed as copy-on-write?

append() to stuct that only has one slice field in golang

I want to append an element to a struct that only consists of a single annonymous slice:
package main
type List []Element
type Element struct {
Id string
}
func (l *List) addElement(id string) {
e := &Element{
Id: id,
}
l = append(l, e)
}
func main() {
list := List{}
list.addElement("test")
}
That does not work, since addElement does not know l as slice but as *List:
go run plugin.go
# command-line-arguments
./plugin.go:13: first argument to append must be slice; have *List
What most likely would work is to go like this:
type List struct {
elements []Element
}
and fix the addElement func accordingly. I there a nicer way than that, eg. one that let me keep the first definition of type List?
Many thanks, sontags
Two problems,
You're appending *Element to []Element, either use Element{} or change the list to []*Element.
You need to dereference the slice in addElement.
Example:
func (l *List) addElement(id string) {
e := Element{
Id: id,
}
*l = append(*l, e)
}

How to remove an item from a slice by calling a method on the slice

Go has stumped me again. Hopefully someone can help. I've created a slice (mySlice) that contains pointers to structs (myStruct).
The problem is the "Remove" method. When we're inside "Remove" everything is fine, but once we return, the slice size hasn't changed, and so we see the last element listed twice.
I originally tried writing "Remove" using the same pattern used in the "Add" method, but it wouldn't compile and has been commented out.
I can get it to work by returning the newly created slice to the calling function, but I don't want to do this because mySlice (ms) is a singleton.
And if I hadn't asked enough already...
The code for the "Add" method is working, although I'm not sure how. From what I can gather "Add" is receiving a pointer to the slice header (the 3 item "struct"). From what I've read, the length and capacity of an slice don't get passed to methods (when passing by value), so perhaps passing a pointer to the slice allows the method to see and use the length and capacity thereby allowing us to "append". If this is true, then why doesn't the same pattern work in "Remove"?
Thanks very much for everyone's insights and help!
package main
import (
"fmt"
)
type myStruct struct {
a int
}
type mySlice []*myStruct
func (slc *mySlice) Add(str *myStruct) {
*slc = append(*slc, str)
}
//does not compile with reason: cannot slice slc (type *mySlice)
//func (slc *mySlice) Remove1(item int) {
// *slc = append(*slc[:item], *slc[item+1:]...)
//}
func (slc mySlice) Remove(item int) {
slc = append(slc[:item], slc[item+1:]...)
fmt.Printf("Inside Remove = %s\n", slc)
}
func main() {
ms := make(mySlice, 0)
ms.Add(&myStruct{0})
ms.Add(&myStruct{1})
ms.Add(&myStruct{2})
fmt.Printf("Before Remove: Len=%d, Cap=%d, Data=%s\n", len(ms), cap(ms), ms)
ms.Remove(1) //remove element 1 (which also has a value of 1)
fmt.Printf("After Remove: Len=%d, Cap=%d, Data=%s\n", len(ms), cap(ms), ms)
}
and the results...
Before Remove: Len=3, Cap=4, Data=[%!s(*main.myStruct=&{0}) %!s(*main.myStruct=&{1}) %!s(*main.myStruct=&{2})]
Inside Remove = [%!s(*main.myStruct=&{0}) %!s(*main.myStruct=&{2})]
After Remove: Len=3, Cap=4, Data=[%!s(*main.myStruct=&{0}) %!s(*main.myStruct=&{2}) %!s(*main.myStruct=&{2})]
You were right the first time with Remove1(). Remove gets a copy of the slice and therefore cannot change the length of the slice.
The issue in your remove function is that according to order of operations in Go, slicing comes before dereferencing.
The fix is to change *slc = append(*slc[:item], *slc[item+1:]...) to *slc = append((*slc)[:item], (*slc)[item+1:]...).
However I would recommend the following for readability and maintainability:
func (slc *mySlice) Remove1(item int) {
s := *slc
s = append(s[:item], s[item+1:]...)
*slc = s
}
Because append would not necessarily return the same address of reference to the slice, as Stephen Weinberg has pointed out.
Another way to workaround with this limitation is defining a struct that wraps the slice.
for example:
package main
import "fmt"
type IntList struct {
intlist []int
}
func (il *IntList) Pop() {
if len(il.intlist) == 0 { return }
il.intlist = il.intlist[:len(il.intlist)-1]
}
func (il *IntList) Add(i... int) {
il.intlist = append(il.intlist, i...)
}
func (il *IntList) String() string {
return fmt.Sprintf("%#v",il.intlist)
}
func main() {
intlist := &IntList{[]int{1,2,3}}
fmt.Println(intlist)
intlist.Pop()
fmt.Println(intlist)
intlist.Add([]int{4,5,6}...)
fmt.Println(intlist)
}
output:
[]int{1, 2, 3}
[]int{1, 2}
[]int{1, 2, 4, 5, 6}

Resources