Duplicating a list to a list with pointers - go

I have simplified the problem incredibly to a small runnable example of my issue, shown below:
package main
import "fmt"
type A struct {
Name string
}
func main() {
main_list := []A{A{"1"}, A{"b"}, A{"3"}}
second_list := make([]*A, 0)
fmt.Println("FIRST LIST:")
for _, x := range main_list {
fmt.Println(x.Name)
second_list = append(second_list, &x)
}
fmt.Println("SECOND LIST:")
for _, x := range second_list {
fmt.Println((*x).Name)
}
}
Which provides:
FIRST LIST:
1
b
3
SECOND LIST:
3
3
3
The simple task is creating main_list with some dummy structs. The real issue is creating references (pointers) from the values in main_list into second_list. I absolutely do not want copies of the structs, only pointers/references to the main_list structs. The second for loop iterates through the newly populated second_list and shows only the last value from main_list (3 in this example) three times.
I expect the issue arrises with the way I am using &x in the first loop. With the way I get all three values of second_list to all be the same struct instance; I am going to assume that I actually made a pointer to the for-loop's iterator (as if it were a reference?). So all the pointers in second_list will always be the last item referenced in the first loop.
The question: How could I make a pointer from what x was pointing to at that moment in the for loop?

You're adding the address of the same x in every call to append.
You could initialize a new x and copy the value:
for _, x := range main_list {
x := x
second_list = append(second_list, &x)
}
Or create a new x directly indexing the slice:
for i := range main_list {
x := main_list[i]
second_list = append(second_list, &x)
}
Or if you want the address of the original value in the slice, you can use:
for i := range main_list {
second_list = append(second_list, &main_list[i])
}

Related

Go dependent type contraints [duplicate]

I am trying to write the following function:
func Fill[X any](slice []*X){
for i := range slice {
slice[i] = new(X)
}
}
xs := make([]*int, 10) // fill with nils
Fill(xs) // now fill with new(int)
That works fine but… if I want to use a slice of interfaces and provide a concrete type?
func Fill[X, Y any](slice []X){
for i := range slice {
slice[i] = new(Y) // not work!
}
}
xs := make([]sync.Locker, 10) // fill with nils
Fill[sync.Locker,sync.Mutex](xs) // ouch
I try some combinations without success, is there a way or go1.18 does not support such relations?
When you constrain both X and Y to any, you lose all interface-implementor relationship. The only thing that is known at compile time is that X and Y are different types, and you can't assign one to the another within the function body.
A way to make it compile is to use an explicit assertion:
func Fill[X, Y any](slice []X) {
for i := range slice {
slice[i] = any(*new(Y)).(X)
}
}
But this panics if Y doesn't really implement X, as in your case, since it is *sync.Mutex (pointer type) that implements sync.Locker.
Moreover, when Y is instantiated with a pointer type, you lose information about the base type, and therefore the zero value, including *new(Y) would be nil, so you don't really have a baseline improvement over make (just typed nils vs. nil interfaces).
What you would like to do is to constrain Y to X, like Fill[X any, Y X](slice []X) but this is not possible because 1) a type parameter can't be used as a constraint; and/or 2) a constraint can't embed a type parameter directly. It also initializes nils as the above.
A better solution is to use a constructor function instead of a second type parameter:
func main() {
xs := make([]sync.Locker, 10)
Fill(xs, func() sync.Locker { return &sync.Mutex{} })
}
func Fill[X any](slice []X, f func() X) {
for i := range slice {
slice[i] = f()
}
}

How to change elements in a list?

If I use container/list as
package main
import (
"container/list"
"fmt"
)
type Data struct {
x int
}
func main() {
l := list.New()
l.PushBack(Data{2})
a := l.Back()
v := a.Value.(Data)
v.x = 1
fmt.Println(l.Back().Value) // --> {2}
}
Well, the value of x in the list does not change. What is the correct programming pattern?
Function arguments and return values are passed by value in Go. Therefore v is a copy of the original Data value. Changing it does not affect the one that is stored in the list.
You can get the behaviour you want by inserting a pointer to a Data value instead:
l := list.New()
l.PushBack(&Data{2})
a := l.Back()
v := a.Value.(*Data)
v.x = 1
fmt.Println(l.Back().Value) // --> &{1}

create two dimensional string array in golang

I need to create a 2 dimensional string array as shown below -
matrix = [['cat,'cat','cat'],['dog','dog']]
Code:-
package main
import (
"fmt"
)
func main() {
{ // using append
var matrix [][]string
matrix[0] = append(matrix[0],'cat')
fmt.Println(matrix)
}
}
Error:-
panic: runtime error: index out of range
goroutine 1 [running]:
main.main()
/tmp/sandbox863026592/main.go:11 +0x20
You have a slice of slices, and the outer slice is nil until it's initialized:
matrix := make([][]string, 1)
matrix[0] = append(matrix[0],'cat')
fmt.Println(matrix)
Or:
var matrix [][]string
matrix = append(matrix, []string{"cat"})
fmt.Println(matrix)
Or:
var matrix [][]string
var row []string
row = append(row, "cat")
matrix = append(matrix, row)
The problem with doing two-dimensional arrays with Go is that you have to initialise each part individually, e.g., if you have a [][]bool, you gotta allocate []([]bool) first, and then allocate the individual []bool afterwards; this is the same logic regardless of whether you're using make() or append() to perform the allocations.
In your example, the matrix[0] doesn't exist yet after a mere var matrix [][]string, hence you're getting the index out of range error.
For example, the code below would create another slice based on the size of an existing slice of a different type:
func solve(board [][]rune, …) {
x := len(board)
y := len(board[0])
visited := make([][]bool, x)
for i := range visited {
visited[i] = make([]bool, y)
}
…
If you simply want to initialise the slice based on a static array you have, you can do it directly like this, without even having to use append() or make():
package main
import (
"fmt"
)
func main() {
matrix := [][]string{{"cat", "cat", "cat"}, {"dog", "dog"}}
fmt.Println(matrix)
}
https://play.golang.org/p/iWgts-m7c4u

iterating over over a 2D slice in go

I am taking the "Tour of Go", and had a question regarding the Exercise: Slices example. Currently I can create the picture by iterating over each index using the the [] operator, just like you could in C.
func Pic(dx, dy int) [][]uint8 {
pic := make([][]uint8, dy)
for i := range pic {
pic[i] = make([]uint8, dx)
for j := range pic[i] {
pic[i][j] = uint8(1)
}
}
return pic
}
However, when I try to do something like below, I get an panic: runtime error: index out of range error. I tried adding print statements and calling Pic(3, 3), which printed out a 3x3 array just fine.
func Pic(dx, dy int) [][]uint8 {
pic := make([][]uint8, dy)
for _, y := range pic {
y = make([]uint8, dx)
for _, x := range y {
x = uint8(1)
_ = x // x has to be used
//fmt.Print("1")
}
//fmt.Print("\n")
}
return pic
}
Any thoughts on what I am doing wrong?
The main problem is your attempt to do assignment. Check my example using your code; https://play.golang.org/p/lwoe79jQ70
What you actually get out of the latter implementation is a 3x0 array, all of the inner arrays are empty. The reason for this is because you're using the range variable for assignment which doesn't work. If the current index is 0, y != pic[0], pic[0] is assigned to y however, y is temporary storage, it typically is the same address and is over written on each iteration. So after the latter example executes, all your x direction arrays are empty, indexing into one causes a panic.
Basically you should just be using your first implementation because it works fine and is the way you would typically do this. But the take away is, when you do a, b := range Something b != Something[a], it is it's on instance, it goes out of scope at the bottom of the loop and assigning to it will not cause a state change to the collection Something, instead you must assign to Something[a] if you want to modify Something[a].
range copies the values from the slice you're iterating over.
See: http://golang.org/ref/spec#RangeClause
To clarify what happens see this simple code example and its output:
package main
import "fmt"
func main() {
s := "hi"
//s[0] = 'H' // cannot assign to s[0]
for _, v := range s {
fmt.Printf("%T, %[1]v, %X\n", v, &v)
v = 'H' // has no effect: this is local var not ref
}
fmt.Println(s)
}
The output is:
int32, 104, C0820042D4
int32, 105, C0820042D4
hi
As you see the address of variable v is not changing (C0820042D4) and v is local variable and range copies value to it, so changing v has no effect.
Here v is rune (int32 alias), A rune is an integer value identifying a Unicode code point, and you cannot assign to s[0] and this won’t compile: s[0] = 'H'
so v = 'H' has no effect on s, it is just local variable.

How to check the uniqueness inside a for-loop?

Is there a way to check slices/maps for the presence of a value?
I would like to add a value to a slice only if it does not exist in the slice.
This works, but it seems verbose. Is there a better way to do this?
orgSlice := []int{1, 2, 3}
newSlice := []int{}
newInt := 2
newSlice = append(newSlice, newInt)
for _, v := range orgSlice {
if v != newInt {
newSlice = append(newSlice, v)
}
}
newSlice == [2 1 3]
Your approach would take linear time for each insertion. A better way would be to use a map[int]struct{}. Alternatively, you could also use a map[int]bool or something similar, but the empty struct{} has the advantage that it doesn't occupy any additional space. Therefore map[int]struct{} is a popular choice for a set of integers.
Example:
set := make(map[int]struct{})
set[1] = struct{}{}
set[2] = struct{}{}
set[1] = struct{}{}
// ...
for key := range(set) {
fmt.Println(key)
}
// each value will be printed only once, in no particular order
// you can use the ,ok idiom to check for existing keys
if _, ok := set[1]; ok {
fmt.Println("element found")
} else {
fmt.Println("element not found")
}
Most efficient is likely to be iterating over the slice and appending if you don't find it.
func AppendIfMissing(slice []int, i int) []int {
for _, ele := range slice {
if ele == i {
return slice
}
}
return append(slice, i)
}
It's simple and obvious and will be fast for small lists.
Further, it will always be faster than your current map-based solution. The map-based solution iterates over the whole slice no matter what; this solution returns immediately when it finds that the new value is already present. Both solutions compare elements as they iterate. (Each map assignment statement certainly does at least one map key comparison internally.) A map would only be useful if you could maintain it across many insertions. If you rebuild it on every insertion, then all advantage is lost.
If you truly need to efficiently handle large lists, consider maintaining the lists in sorted order. (I suspect the order doesn't matter to you because your first solution appended at the beginning of the list and your latest solution appends at the end.) If you always keep the lists sorted then you you can use the sort.Search function to do efficient binary insertions.
Another option:
package main
import "golang.org/x/tools/container/intsets"
func main() {
var (
a intsets.Sparse
b bool
)
b = a.Insert(9)
println(b) // true
b = a.Insert(9)
println(b) // false
}
https://pkg.go.dev/golang.org/x/tools/container/intsets
This option if the number of missing numbers is unknown
AppendIfMissing := func(sl []int, n ...int) []int {
cache := make(map[int]int)
for _, elem := range sl {
cache[elem] = elem
}
for _, elem := range n {
if _, ok := cache[elem]; !ok {
sl = append(sl, elem)
}
}
return sl
}
distincting a array of a struct :
func distinctObjects(objs []ObjectType) (distinctedObjs [] ObjectType){
var output []ObjectType
for i:= range objs{
if output==nil || len(output)==0{
output=append(output,objs[i])
} else {
founded:=false
for j:= range output{
if output[j].fieldname1==objs[i].fieldname1 && output[j].fieldname2==objs[i].fieldname2 &&......... {
founded=true
}
}
if !founded{
output=append(output,objs[i])
}
}
}
return output
}
where the struct here is something like :
type ObjectType struct {
fieldname1 string
fieldname2 string
.........
}
the object will distinct by checked fields here :
if output[j].fieldname1==objs[i].fieldname1 && output[j].fieldname2==objs[i].fieldname2 &&......... {

Resources