Adding arrays (from a map) to a slice of slices in Go - arrays

I have a map with arrays for keys.
I want to add all of the keys/arrays to a slice of slices.
However, I am getting unexpected results.
Code:
package main
import "fmt"
func main() {
var finalResult [][]int
set := make(map[[2]int]int)
a, b, c := [2]int{1, 1}, [2]int{2, 2}, [2]int{3, 3}
set[a], set[b], set[c] = 1, 1, 1
fmt.Println("set: ", set)
for x := range set {
fmt.Println("\nfinalResult 0: ", finalResult)
fmt.Println("x: ", x[:])
finalResult = append(finalResult, x[:])
fmt.Println("finalResult 1: ", finalResult)
}
}
Output:
set: map[[1 1]:1 [2 2]:1 [3 3]:1]
finalResult 0: []
x: [1 1]
finalResult 1: [[1 1]]
finalResult 0: [[2 2]]
x: [2 2]
finalResult 1: [[2 2] [2 2]]
finalResult 0: [[3 3] [3 3]]
x: [3 3]
finalResult 1: [[3 3] [3 3] [3 3]]
finalResult appears to be changing during the for loop?
Can someone explain what is happening and how I can work around this issue?

The problem is, that you append the x local variable to the finalResult, which is a pointer, so in every for loop, the x will point to a new array in the memory. When you add this x three times to the finalResult and print it, all the three x will be points to the same memory address. You have to copy the content where x points in every loop to a new variable and add this to the finalResult.
package main
import "fmt"
func main() {
var finalResult [][]int
set := make(map[[2]int]int)
a, b, c := [2]int{1, 1}, [2]int{2, 2}, [2]int{3, 3}
set[a], set[b], set[c] = 1, 1, 1
fmt.Println("set: ", set)
for x := range set {
fmt.Println("\nfinalResult 0: ", finalResult)
fmt.Println("x: ", x[:])
a := make([]int, 2)
copy(a, x[:])
finalResult = append(finalResult, a)
fmt.Println("finalResult 1: ", finalResult)
}
}
But be aware, that ranging over a map will be always in random order, so your final result may change on every run.

Related

some question about append built-in function

package main
import (
"fmt"
)
func main() {
result := subsets([]int{9,3,0,1,2})
fmt.Println(result)
}
func subsets(nums []int) [][]int {
result := [][]int{}
var fun func([]int, int)
fun = func (preSets []int, start int) {
// fmt.Println(start, preSets, result)
result = append(result, preSets)
// fmt.Println(result)
for idx := start; idx < len(nums); idx++ {
tmp := nums[idx]
newSet := append(preSets, tmp)
fun(newSet, idx+1)
newSet = newSet[:len(newSet) - 1]
}
}
fun([]int{}, 0)
return result
}
i want to find the subsets of a slice, and think the code above should work. but it give me the following output
[[] [9] [9 3] [9 3 0] [9 3 0 2] [9 3 0 1 2] [9 3 0 2] [9 3 1] [9 3 1 2] [9 3 2] [9 0] [9 0 1] [9 0 1 2] [9 0 2] [9 1] [9 1 2] [9 2] [3] [3 0] [3 0 1] [3 0 1 2] [3 0 2] [3 1] [3 1 2] [3 2] [0] [0 1] [0 1 2] [0 2] [1] [1 2] [2]]
the fifth slice should be [9 3 0 1], but it is [9 3 0 2],and i print the result each step, i found out that the fifth slice turned from [9301] to [9302] when the seventh slice [9302] is appended,i think it should be related to the array storage under the slice, but why
i think [the problem is] related to the array storage under the slice
Yes, it is.
but why
The append function:
sometimes re-uses the original backing array, but
sometimes creates a new backing array and copies the original values to the new array.
There are no promises made about when it does one of these two.
When it re-uses the original array, you encounter the behavior you dislike. When it makes a new array, you encounter the behavior you desire. Since you want the copy behavior, you can simply write your own "always copy" code and get the behavior you want. However, a more-minimal change is to replace:
result = append(result, preSets)
with:
result = append(result, unshare(preSets))
with the function unshared defined as:
func unshare(a []int) []int {
tmp := make([]int, len(a))
copy(tmp, a)
return tmp
}
If you are trying to explain to yourself exactly why you got the exact result that you got, it's a little tricky because you're not promised anything about when append makes a new backing array and when it re-uses the existing backing array—but it is explainable, if you make the following two assumptions:
append never makes a copy of the backing array if it does not have to; and
when append does make a copy, it sometimes over-allocates the new backing array by some factor.
That is, append behaves more or less like this, except of course this one is specific to []int:
func xappend(orig []int, add []int) []int {
has := len(orig)
needed := has + len(add)
// If we can fit the added elements in, do that now.
if cap(orig) >= needed {
fmt.Println("copy in place")
orig = orig[:needed]
copy(orig[has:needed], add)
return orig
}
newSize := undocumentedFunction(has, cap(orig), needed)
fmt.Println("make new, size", newSize)
new := make([]int, needed, newSize)
copy(new, orig)
copy(new[has:needed], add)
return new
}
func undocumentedFunction(oldCap, oldLen, newCap int) int {
twiceAsMuch := oldCap + oldCap
if newCap > twiceAsMuch {
return newCap
}
// 2x old capacity is enough, but
// we'll tweak this in various ways.
// The exact tweaks might change at any time.
if oldLen < 1024 {
return twiceAsMuch
}
panic("didn't copy this part")
}
This "undocumented function" is part of the runtime system, and it collaborates with the compiler. You can see its actual code here (note: this link may break or go to the wrong line at some point).
(Sample runnable code here on the Go Playground.)

Go slice element replaced by last element

My problem is the answer should has [1 1 2 2], but it got [1 1 2 3].
I debuged this code, found that in that loop, [1 1 2 2] was appended to the array firstly, but it changed to [1 1 2 3] in next loop.
I don't understand why, the log which started with 'D', the elements in this array changed in next loop.
Here is my code:
https://play.golang.org/p/X_CU8GlMOqf
package main
import (
"fmt"
)
func main() {
nums := []int{1, 1, 2, 2, 3}
result := subsetsWithDup(nums)
fmt.Println(result)
}
func subsetsWithDup(nums []int) [][]int {
result := [][]int{{}, nums}
for i := 0; i < len(nums); i++ {
if i > 0 && nums[i] == nums[i-1] {
continue
}
for j := 1; j < len(nums); j++ {
result = append(result, dsf(nums, []int{nums[i]}, i+1, j)...)
}
}
return result
}
func dsf(nums []int, set []int, start int, length int) [][]int {
result := [][]int{}
if len(set) == length {
return append(result, set)
}
for i := start; i < len(nums); i++ {
if i != start && nums[i] == nums[i-1] {
continue
}
if len(set) == 3 {
fmt.Printf("A %v %p\n", result, &result)
}
tmp := set[:]
tmp = append(tmp, nums[i])
if len(set) == 3 {
fmt.Printf("B %v %p %v %p\n", tmp, &tmp, result, &result)
}
result = append(result, dsf(nums, tmp, i+1, length)...)
if len(tmp) == 4 {
fmt.Printf("C %v %p %v %p\n", tmp, &tmp, result, &result)
for _, r := range result {
fmt.Printf("D %v %p\n", r, &r)
}
}
}
return result
}
A [] 0xc000004960
B [1 1 2 2] 0xc0000049c0 [] 0xc000004960
C [1 1 2 2] 0xc0000049c0 [[1 1 2 2]] 0xc000004960
D [1 1 2 2] 0xc000004ae0
A [[1 1 2 2]] 0xc000004960
B [1 1 2 3] 0xc000004b60 [[1 1 2 3]] 0xc000004960
C [1 1 2 3] 0xc000004b60 [[1 1 2 3] [1 1 2 3]] 0xc000004960
D [1 1 2 3] 0xc000004ca0
D [1 1 2 3] 0xc000004ca0
A [] 0xc000004da0
B [1 2 2 3] 0xc000004de0 [] 0xc000004da0
C [1 2 2 3] 0xc000004de0 [[1 2 2 3]] 0xc000004da0
D [1 2 2 3] 0xc000004f00
You are trying to copy a slice with:
tmp := set[:]
This does not copy the slice, it re-uses the original slice. It has the same effect as tmp := set. You can use:
tmp := append([]int(nil), set...)
if you like. Here is your program with this change, and all the extra debug commented out.

selecting 2D sub-slice of a 2D-slice using ranges in go

I'm getting a surprising result when selecting a 2D sub-slice of a slice.
Consider the following 2D int array
a := [][]int{
{0, 1, 2, 3},
{1, 2, 3, 4},
{2, 3, 4, 5},
{3, 4, 5, 6},
}
To select the top left 3x3 2D slice using ranges I would use
b := a[0:2][0:2]
I would expect the result to be
[[0 1 2] [1 2 3] [2 3 4]]
however the second index range doesn't seem to have any effect, and returns the following instead:
[[0 1 2 3] [1 2 3 4] [2 3 4 5]]
What am I missing? Can you simply not select a sub-slice like this where the dimension > 1 ?
You can't do what you want in a single step. Slices and arrays are not 2-dimensional, they are just composed to form a multi-dimensional object. See How is two dimensional array's memory representation
So with a slice expression, you just get a slice that will hold a subset of the "full" rows, and its type will be the same: [][]int. If you slice it again, you just slicing the slice of rows again.
Also note that the higher index in a slice expression is exclusive, so a[0:2] will only have 2 rows, so you should use a[0:3] or simply a[:3] instead.
To get what you want, you have to slice the rows individually like this:
b := a[0:3]
for i := range b {
b[i] = b[i][0:3]
}
fmt.Println(b)
This will output (try it on the Go Playground):
[[0 1 2] [1 2 3] [2 3 4]]
Or shorter:
b := a[:3]
for i, bi := range b {
b[i] = bi[:3]
}

Slice array of arbitrary dimension with lists of start and end indices

I need to copy a part of a 3D array.
I have the indexes of start and end of the copy.
For example 2D array:
[[2 2 3 4 5]
[2 3 3 4 5]
[2 3 4 4 5]
[2 3 4 5 5]
[2 3 4 5 6]]
starting index, end index are:
mini = [2, 1]
maxi = [4, 3]
So the result should be:
[[3 4 4]
[3 4 5]]
I can write:
result = matrix[mini[0]:maxi[0], mini[1]:maxi[1]]
Is there a way to do it generally ? for 3Dim or NDim arrays ?
The trick here is realizing what the indexing syntax is under the hood. This:
result = matrix[mini[0]:maxi[0], mini[1]:maxi[1]]
Is shorthand in python (not just numpy) for:
indices = slice(mini[0], maxi[0]), slice(mini[1], maxi[1])
result = matrix[indices]
So we just need to generate indices dynamically:
lower = [2, 1, ...]
upper = [4, 3, ...]
indices = tuple(np.s_[l:u] for l, u in zip(lower, upper))
result = matrix_nd[indices]
np.s_[a:b] is a shorthand for slice(a, b). Here we build a tuple containing as many slices as you have values in lower and upper
What you are looking for is the slice object, see that example:
matrix = np.random.rand(4,5)
mini = [2, 1]
maxi = [4, 3]
slices=[slice(b,e) for b, e in zip(mini,maxi)]
print(slices)
print(matrix[slices])
print(matrix[mini[0]:maxi[0], mini[1]:maxi[1]])

Function for copying arrays in Go language

Is there any built-in function in Go for copying one array to another?
Will this work in case of two (or more) dimensional arrays?
Is there any built-in function in Go language for copying one array to another?
Yes: http://play.golang.org/p/_lYNw9SXN5
a := []string{
"hello",
"world",
}
b := []string{
"goodbye",
"world",
}
copy(a, b)
// a == []string{"goodbye", "world"}
Will this work in case of two (or more) dimensional arrays?
copy will do a shallow copy of the rows: http://play.golang.org/p/0gPk6P1VWh
a := make([][]string, 10)
b := make([][]string, 10)
for i := range b {
b[i] = make([]string, 10)
for j := range b[i] {
b[i][j] = strconv.Itoa(i + j)
}
}
copy(a, b)
// a and b look the same
b[1] = []string{"some", "new", "data"}
// b's second row is different; a still looks the same
b[0][0] = "apple"
// now a looks different
I don't think there's a built-in for doing deep-copys of multi-dimensional arrays: you can do it manually like: http://play.golang.org/p/nlVJq-ehzC
a := make([][]string, 10)
b := make([][]string, 10)
for i := range b {
b[i] = make([]string, 10)
for j := range b[i] {
b[i][j] = strconv.Itoa(i + j)
}
}
// manual deep copy
for i := range b {
a[i] = make([]string, len(b[i]))
copy(a[i], b[i])
}
b[0][0] = "apple"
// a still looks the same
edit: As pointed out in the comments, I assumed by "copy an array" you meant "do a deep copy of a slice", as arrays can be deep-copied with the = operator as per jnml's answer (because arrays are value types): http://play.golang.org/p/8EuFqXnqPB
The primary "function" for copying an array in Go is the assignment operator =, as it is the case for any other value of any other type.
package main
import "fmt"
func main() {
var a, b [4]int
a[2] = 42
b = a
fmt.Println(a, b)
// 2D array
var c, d [3][5]int
c[1][2] = 314
d = c
fmt.Println(c)
fmt.Println(d)
}
Playground
Output:
[0 0 42 0] [0 0 42 0]
[[0 0 0 0 0] [0 0 314 0 0] [0 0 0 0 0]]
[[0 0 0 0 0] [0 0 314 0 0] [0 0 0 0 0]]
Use copy http://play.golang.org/p/t7P6IliMOK
a := []int{1, 2, 3}
var b [3]int
fmt.Println("A:", a)
fmt.Println("B:", b)
copy(b[:], a)
fmt.Println("A:", a)
fmt.Println("B2:", b)
b[1] = 9
fmt.Println("A:", a)
fmt.Println("B3:", b)
OUT:
A: [1 2 3]
B: [0 0 0]
A: [1 2 3]
B2: [1 2 3]
A: [1 2 3]
B3: [1 9 3]

Resources