Related
I want to copy an array but the copied array always makes changes to the original array as well. Why is this the case?
Below is a code example of the issue:
package main
import (
"fmt"
)
func main() {
array := make([]int, 2)
for i := range array {
array[i] = 0
}
oldArray := array
array[0] = 3 // why does this also change oldArray?
oldArray[1] = 2 // why does this also change the original array?
fmt.Println(oldArray, array)
// I expected [0,2], [3,0]
// but it returns [3,2], [3,2]
}
I tried to initiate the variable oldArray before array but the result is the same.
In your example code, you're working with slices, not arrays.
From the slice documentation:
A slice is a descriptor for a contiguous segment of an underlying array and provides access to a numbered sequence of elements from that array.
When you assign a slice to a variable, you're creating a copy of that descriptor and therefore dealing with the same underlying array. When you're actually working with arrays, it has the behavior you're expecting.
Another snippet from the slice documentation (emphasis mine):
A slice, once initialized, is always associated with an underlying array that holds its elements. A slice therefore shares storage with its array and with other slices of the same array; by contrast, distinct arrays always represent distinct storage.
Here's a code sample (for the slices, the memory address of the first element is in parentheses, to clearly point out when two slices are using the same underlying array):
package main
import (
"fmt"
)
func main() {
// Arrays
var array [2]int
newArray := array
array[0] = 3
newArray[1] = 2
fmt.Printf("Arrays:\narray: %v\nnewArray: %v\n\n", array, newArray)
// Slices (using copy())
slice := make([]int, 2)
newSlice := make([]int, len(slice))
copy(newSlice, slice)
slice[0] = 3
newSlice[1] = 2
fmt.Printf("Slices (different arrays):\nslice (%p): %v \nnewSlice (%p): %v\n\n", slice, slice, newSlice, newSlice)
// Slices (same underlying array)
slice2 := make([]int, 2)
newSlice2 := slice2
slice2[0] = 3
newSlice2[1] = 2
fmt.Printf("Slices (same array):\nslice2 (%p): %v \nnewSlice2 (%p): %v\n\n", slice2, slice2, newSlice2, newSlice2)
}
Output:
Arrays:
array: [3 0]
newArray: [0 2]
Slices (different arrays):
slice (0xc000100040): [3 0]
newSlice (0xc000100050): [0 2]
Slices (same array):
slice2 (0xc000100080): [3 2]
newSlice2 (0xc000100080): [3 2]
Go Playground
use copy function.
oldArray := make([]int, len(array))
copy(oldArray, array)
https://play.golang.org/p/DsLJ2PDIy_N
A basic question that I'm struggling to find an answer for as there are a lot of answers about how to join two slices using the append function and the spread operator which erroneously use the word 'array'.
I am new to Go and have made the assumption that using sized arrays is good practice where the size is known. However I am struggling to work with arrays as I can't figure out how to do simple operations such as concatenation. Here is some code.
var seven [7]int
five := [5]int{1,2,3,4,5}
two := [2]int{6,7}
//this doesn't work as both the inputs and assignment are the wrong type
seven = append(five,two)
//this doesn't work as the assignment is still the wrong type
seven = append(five[:],two[:])
//this works but I'm not using arrays anymore so may as well use slices everywhere and forget sizing
seven2 := append(five[:],two[:])
As far as I can see I can either just give up on arrays and use slices exclusively or I could write a loop to explicitly construct the new array. Is there a third option?
append() can only be used to append elements to a slice. If you have an array, you can't pass that directly to append().
What you may do is slice the array, so you get a slice (which will use the array as its backing store), and you can use that slice as the target and source of elements.
For example:
s := seven[:0]
s = append(s, five[:]...)
s = append(s, two[:]...)
fmt.Println(seven)
This will print (try it on the Go Playground):
[1 2 3 4 5 6 7]
Also note that since append() returns the resulting slice, it's possible to write all this in one line:
_ = append(append(seven[:0], five[:]...), two[:]...)
(Storing the result is not needed here because we have and want to use only the backing array, but in general that is not the case.)
This outputs the same, try it on the Go Playground. Although this isn't very readable, so it's not worth compacting it into a single line.
Although when you have the target array, "appending" arrays is nothing more than copying them to the target, to the proper position. For that, you may use the builtin copy() function too. Note that the copy() function also accepts only slices, so you have to slice the arrays here too.
copy(seven[:], five[:])
copy(seven[len(five):], two[:])
fmt.Println(seven)
This will output the same. Try this one on the Go Playground.
You can use copy
copy(seven[:], five[:])
copy(seven[5:], two[:])
fmt.Printf("%v\n", seven)
> [1 2 3 4 5 6 7]
You can concatenate two arrays in go using copy function
package main
import "fmt"
func main() {
five := [5]int{1, 2, 3, 4, 5}
two := [2]int{6, 7}
var n [len(five) + len(two)]int
copy(n[:], five[:])
copy(n[len(five):], two[:])
fmt.Println(n)
}
https://blog.golang.org/go-slices-usage-and-internals
Golang runtime used to check whether current index exceeds the maximum possible.
On the side of array, it look ups its type (which contain its len and reference to the element type), because that's type, that can be registered only at compile time.
// each array mention with unique size creates new type
array := [5]byte{1,2,3,4,5}
On the side of slice, it look ups their header which looks like:
type slice {
data *byte
len int
cap int // capacity, the maximum possible index
}
As you can see, any slice is a single structure with data and len, cap fields, meanwhile array is just single pointer to data (*byte).
When you trying to convert array to slice, it just creates slice header and fills fields with:
slice := array[:]
==
slice := Slice{}
slice.data = array
slice.len = type_of(array).len
slice.cap = type_of(array).len
you can do that simply by converting array into slice:
arr1 := [...]int {1,2,3,}
arr2 := [...]int {4,5,6, }
//arr3 = arr1 + arr2 // not allowed
// converting arrays into slice
slc_arr1, slc_arr2 := arr1[:], arr2[:]
slc_arr3 := make([]int, 0)
slc_arr3 = append(slc_arr1, slc_arr2...)
fmt.Println(slc_arr3) // [1 2 3 4 5 6]
There is a more general way of appending an array of any type(once Golang has generics, but for now this solution is specific to strings. Just change the type as appropriate). The notion of Fold comes from Functional Programming. Note I have also included a filter function which also uses Fold. The solution is not stack safe but in many cases that does not matter. It can be made stack safe with trampolining. At the end is an example of its usage.
func FoldRightStrings(as, z []string, f func(string, []string) []string) []string {
if len(as) > 1 { //Slice has a head and a tail.
h, t := as[0], as[1:len(as)]
return f(h, FoldRightStrings(t, z, f))
} else if len(as) == 1 { //Slice has a head and an empty tail.
h := as[0]
return f(h, FoldRightStrings([]string{}, z, f))
}
return z
}
func FilterStrings(as []string, p func(string) bool) []string {
var g = func(h string, accum []string) []string {
if p(h) {
return append(accum, h)
} else {
return accum
}
}
return FoldRightStrings(as, []string{}, g)
}
func AppendStrings(as1, as2 []string) []string {
var g = func(h string, accum []string) []string {
return append(accum, h)
}
return FoldRightStrings(as1, as2, g)
}
func TestAppendStringArrays(t *testing.T) {
strings := []string{"a","b","c"}
bigarray := AppendStrings(AppendStrings(strings, strings),AppendStrings(strings, strings))
if diff := deep.Equal(bigarray, []string{"a","b","c","c","b","a","a","b","c","c","b","a"}); diff != nil {
t.Error(diff)
}
}
I am having a hard time learning how to loop through a string in Go to do some stuff (specifically, to separate words than contain vowels).
I wrote this code snippet: https://play.golang.org/p/zgDtOyq6qf.
Here is the error I’m getting when running it:
panic: runtime error: index out of range
goroutine 1 [running]:
panic(0x1045a0, 0x1040a010)
/usr/local/go/src/runtime/panic.go:500 +0x720
main.myFunc(0x114130, 0x4, 0x0, 0x0, 0x0, 0x3ba3)
/tmp/sandbox960520145/main.go:19 +0x1a0
main.main()
/tmp/sandbox960520145/main.go:10 +0x40
I searched in this forum, and someone said that it’s due to the length of the array, but it’s not the case here. I cannot figure out how to solve this issue.
Can someone please suggest something?
First let's explain:
result := make([]string, 0, 4)
The make built-in function allocates and initializes an object of type []string call it Slice of string
Slice internals:
A slice is a descriptor of an array segment. It consists of a pointer
to the array, the length of the segment, and its capacity (the maximum
length of the segment).
So result := make([]string, 0, 4) allocates and initializes an object of type []string with length = 0 and capacity = 4.
And result := make([]string, 4, 4) allocates and initializes an object of type []string with length = 4 and capacity = 4, which is equal to result := make([]string, 4).
Now what is the difference between result := make([]string, 0, 4) and result := make([]string, 4):
With result := make([]string, 0, 4) the underlying array of this Slice is empty meaning using result[0] will panic: runtime error: index out of range.
With result := make([]string, 4) the underlying array of this Slice has 4 string elements, meaning using result[0], result[1], result[2], result[3] is OK:
package main
import "fmt"
func main() {
result := make([]string, 4)
fmt.Printf("%q, %q, %q, %q \n", result[0], result[1], result[2], result[3])
}
output:
"", "", "", ""
And result := make([]string, 4) is equal to result := []string{"", "", "", ""} meaning this code:
package main
import "fmt"
func main() {
result := []string{"", "", "", ""}
fmt.Printf("%q, %q, %q, %q \n", result[0], result[1], result[2], result[3])
}
output is the same as above code:
"", "", "", ""
The append built-in function
appends elements to the end of a slice. If it has sufficient capacity,
the destination is resliced to accommodate the new elements. If it
does not, a new underlying array will be allocated. Append returns the
updated slice. It is therefore necessary to store the result of
append, often in the variable holding the slice itself:
slice = append(slice, elem1, elem2)
slice = append(slice, anotherSlice...)
As a special case, it is legal to append a string to a byte slice,
like this:
slice = append([]byte("hello "), "world"...)
Now in your code inside function myFunc after result := make([]string, 0, 4), you may use append, like this working code (The Go Playground):
package main
import (
"fmt"
"strings"
)
func main() {
strs := strings.Fields("Political srt")
fmt.Println(len(strs)) // It's not empty so why index out of range
fmt.Println(strs, strs[0], strs[1])
fmt.Println(strings.ContainsAny(strs[0], "eaiuo"))
fmt.Println(myFunc("Political srt"))
}
func myFunc(input string) []string {
strs := strings.Fields(input)
result := make([]string, 0, 4)
for i := 0; i < len(strs); i++ {
if strings.ContainsAny(strs[i], "eaiu") {
result = append(result, strs[i])
} else {
result = append(result, strs[i])
}
}
return result
}
You may simplify that code, like this working code (The Go Playground):
package main
import (
"fmt"
"strings"
)
func main() {
fmt.Println(myFunc("Political srt"))
}
func myFunc(input string) []string {
strs := strings.Fields(input)
result := make([]string, 0, 4)
for _, s := range strs {
if strings.ContainsAny(s, "eaiu") {
result = append(result, s)
}
}
return result
}
The issue is that you are creating a slice with length 0, but with a maximum capacity of 4, but at the same time you are trying to allocate already a value to the zeroth index of the slice created, which is normally empty. This is why you are receiving the index out of range error.
result := make([]string, 0, 4)
fmt.Println(len(result)) //panic: runtime error: index out of range
You can change this code with:
result := make([]string, 4)
which means the capacity will be the same length as the slice length.
fmt.Println(cap(result)) // 4
fmt.Println(len(result)) // 4
You can read about arrays, slices and maps here: https://blog.golang.org/go-slices-usage-and-internals
Index out of range error is appearing because you are not initializing the result array with sufficient length.
In myFunc, you have:
result := make([]string, 0, 4)
This creates a slice of strings which has an underlying array of length 4, but since you have declared slice length to be 0, none of the elements from underlying array are accessible to the slice. So even result[0] is out of range of the available indices.
To fix this, simply supply a sufficiently large length parameter to make:
result := make([]string, 4, 4)
You can read more about how slices operate here.
Is it possible to use range and len on a multidimensional array?
Either with var a [3]int8 or
package main
func main () {
var a [3][5]int8
for h := range a {
println(h)
}
println(len(a))
}
Both produce
0
1
2
3
?
Thanks to dystroy's answer, here's an example of writing and reading a 3-dimensional array i was able to adapt (posting here because I had much trouble finding any examples of this, so maybe this will help someone else):
package main
func main() {
var a [3][5][7]uint8
//write values to array
for x, b := range a {
for y, c := range b {
for z, _ := range c {
a[x][y][z] = uint8(x*100+y*10+z)
}
}
}
//read values from array
for _, h := range a {
for _, i := range h {
for _, j := range i {
print(j, "\t")
}
println()
}
println()
}
}
In Go as in most languages, what you call a multidimensional array is an array of arrays. The len operator only gives you the length of the "external" array.
Maybe the var declaration could be clearer for you if you see it as
var a [3]([5]int8)
which also compiles. It's an array of size 3 whose elements are arrays of size 5 of int8.
package main
import "fmt"
func main() {
var a [3][5]int8
for _, h := range a {
fmt.Println(len(h)) // each one prints 5
}
fmt.Println(len(a)) // prints 3, the length of the external array
}
outputs
5
5
5
3
To loop safely through the whole matrix, you can do this :
for _, h := range a {
for _, cell := range h {
fmt.Print(cell, " ")
}
fmt.Println()
}
If you need to change the content, you may do
for i, cell := range h { // i is the index, cell the value
h[i] = 2 * cell
}
The solution with range is already provided so i'll talk about how to use length (len) to go through a multi-dimesional array in golang.
So if u have an array arr[][] :
[1,2,3]
[4,5,6]
Now the len(arr) will output = 2.
while len(arr[1]) will output = 3.
Sample code is provided here : https://play.golang.org/p/XerzPhkQrhU
No, the first one produces 0,1,2 ( index of in the range )
http://play.golang.org/p/0KrzTRWzKO
And the second one produces 3 ( the length of the array ).
http://play.golang.org/p/0esKFqQZL0
In both cases you're using the outermost array.
Having read the following at http://golang.org/doc/effective_go.html#arrays...
Arrays are values. Assigning one array to another copies all the
elements.
In particular, if you pass an array to a function, it will receive a copy
of the array, not a pointer to it.
... I expect in the following code that arr2 to be distinct from arr, and main()'s arr to be distinct from shuffle()'s arr. Can someone please explain why the following code shuffles arr2? I know Go is still a young language; perhaps the treatment of arrays has changed?
package main
import (
"fmt"
"rand"
"time"
)
func shuffle(arr []int) {
rand.Seed(time.Nanoseconds())
for i := len(arr) - 1; i > 0; i-- {
j := rand.Intn(i)
arr[i], arr[j] = arr[j], arr[i]
}
}
func main() {
arr := []int{1, 2, 3, 4, 5}
arr2 := arr
shuffle(arr)
for _, i := range arr2 {
fmt.Printf("%d ", i)
}
}
I think your problem is that you're confusing arrays and slices.
Arrays are fixed-length lists of values. You're actually not using any arrays in your example. Arrays can be declared a few ways:
arr1 := [3]int{1, 2, 3} // an array of 3 integers, 1-3
arr2 := [...]int{1, 2, 3} // same as the previous line, but we're letting
// the compiler figure out the size of the array
var arr3 [3]int // a zeroed out array of 3 integers
You're using slices. A slice is a reference to an underlying array. There are a few ways to allocate new slices:
slice1 := []int{1, 2, 3} // a slice of length 3 containing the integers 1-3
slice2 := make([]int, 3) // a slice of length 3 containing three zero-value integers
slice3 := make([]int, 3, 5) // a slice of length 3, capacity 5 that's all zeroed out
Any other slice assignments are just duplicating a reference to an array.
Now that we've established that, the line
arr := []int{1, 2, 3, 4, 5}
creates a slice referencing an anonymous underlying array that contains the numbers 1-5.
arr2 := arr
duplicates that reference -- it does not copy the underlying array. So there's one underlying array and two references to it. That's why the contents of arr2 change when you modify the contents of arr. They're referencing the same array.