Array of slices - GO - arrays

1)
For below slice declaration,
var a [][3]string
creates a single slice(a) that points to array of 3 strings, len(a) = 3 and cap(a) =3 but not cap(a) >= 3
a = make([][3]string, 5)
creates 5 slices, where each slice(say a[0]) points to array of 3 strings, len(a[0]) = 3 and cap(a[0]) = 3
2)
For slice declaration,
var b [][3][6]string
creates a single slice(b) that points to 3 arrays of 6 strings each, where len(b) = 3 and cap(b) =3
b = make([][3][6]string, 5)
creates 5 slices, where each slice(say b[0]) points to 3 arrays of 6 strings each
In both cases, after making slices, I said, creates 5 slices,
Considering the syntax, below are my two questions,
a = make([][3]string, 5)
My question:
1)
Is it 5 slices, where each slice(a[0]) is array of 3 strings?
or
Is it a single slice(a) pointing to 5 arrays of 3 strings each?
2)
How do I know the type of an element? slice or array?
1) In such scenarios, "array of" terminology has to be cautiously used.
2) There are no implicit pointers involved in GO unlike C

Slices and arrays are two different types: an array in memory is a contiguous sequences of values of the same type. In Go a type has a fixed size. The very same concept is present for example in C++ and
int x[5]; // C and C++
x [5]int // Go
are basically the same thing (not 100% the same because arrays in C and C++ are "second class citizens" and behave strangely in a few places, Go is more uniform on that).
A slice in Go is instead a "view" of a portion of an array, and is more or less equivalent to a C++ structure with a pointer to the first element, a number of used elements (relative to first) and a number of available elements (relative to first)
// C++
template<typename T>
struct Slice {
T *first;
int size, capacity;
};
Slice<int> x{nullptr, 0, 0};
// Go
var x []int
The Make special function is able to create slices associated to newly created arrays, given size and optional capacity:
// C++
template<typename T>
Slice<T> make(int size, int capacity=-1) {
if (capacity == -1) capacity = size;
return Slice<T>{new T[capacity], size, capacity};
}
// Go
x := Make([]int, size, capacity)
Slices can be efficiently passed around in O(1) (independently on size/capacity) and you can also take a sub-slice of a slice in O(1)... note that Go is garbage collected, doing the same in C++ would require some extra work (for example also keeping a pointer to the original array object and its size in the slice).
You can of course have arrays of slices, slices of slices, slices of arrays and arrays of arrays. Note however that in Go Make is used only to create slices (and maps) not for arrays...
x := Make([][2]int, 3) // a slice of 3 arrays of 2 ints each
// Same thing (except for content)
y := [][2]int{[2]int{1, 2},
[2]int{3, 4},
[2]int{5, 6}}
// An array of 3 slices of two ints each
z := [3][]int{[]int{1, 2},
[]int{3, 4},
[]int{5, 6}}
While for example both y and z in the playground would look the same [[1, 2], [3, 4], [5, 6]] when using fmt.Println, they are very different types, for example you can add a new pair to y with
y = append(y, [2]int{7, 8}) // now [[1, 2], [3, 4], [5, 6], [7, 8]]
and you can instead increase the length of first element of z with
z[0] = append(z[0], 99) // now [[1, 2, 99], [3, 4], [5, 6]]
but you cannot add new elements to z (it's an array) and you cannot extend an element of y (because elements are arrays)

1) Considering the syntax,
a = make([][3]string, 5)
Is it 5 slices, where each slice(a[0]) is array of 3 strings?
or
Is it a single slice(a) pointing to 5 arrays of 3 strings each?
Its a single slice with 5 elements where each element is an array of 3 strings.
2)
How do I know the type of an element? slice or array?
Slices and arrays are different types. You can not interchangeably assign arrays and slices to the same variable, hence if the declaration declares it as an array, its an array, if it declares it as a slice, its a slice. If it has a number in the brackets ([5]string) its an array, if the brackets are empty ([]string) its a slice.
2) For slice declaration,
var b [][3][6]string
creates a single slice(b) that points to 3 arrays of 6 strings each,
where len(b) = 3 and cap(b) =3
b = make([][3][6]string, 5)
creates 5 slices, where each slice(say b[0]) points to 3 arrays of 6 strings each
No. Former code just declares a variable, it doesn't hold a slice yet. Latter code creates one slice with five elements.

Related

How to copy array without changing original array?

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

Removing elements in a slice

Go does not provide any high level functions to remove elements from a slice. I wrote a function that removes given value from a slice in a way that typically suggested here, but it produced quite unexpected result.
package main
import "fmt"
type Area struct {
Cells [2][]uint8
}
func main() {
var area1 Area
area1.Cells[1] = []uint8 {5, 6, 7}
area2 := area1
area1.Cells[1] = removeValueFromCell(area1.Cells[1], 6)
fmt.Println(area1.Cells[1])
fmt.Println(area2.Cells[1])
}
func removeValueFromCell(cell []uint8, value uint8) []uint8{
var res = cell
for i := 0; i < len(cell); i++ {
if cell[i] == value {
res = append(cell[:i], cell[i+1:]...)
}
}
return res
}
This program outputs:
[5 7] <- as expected
[5 7 7] <- why not [5 6 7] or [5 7] ?
Slice values are just headers, pointing to a backing array. The slice header only contains the pointer. So when you copy a slice value, the copy will also point to the same backing array. So if you change the backing array via the original slice header, the copy will also observe the changes.
This is what happens in your case. You assign area1 to area2. Cells is an array of slices. So the array will be copied, which contains slice headers, so slice headers will be copied. Slice headers contain pointers to backing arrays, the backing arrays will not be duplicated.
So there is only one backing array holding the [5, 6, 7] elements. Then calling removeValueFromCell(), it will modify this backing array:
Before:
[5, 6, 7]
After:
[5, 7, 7]
Because the element 6 was removed, and the rest of the slice (the elements [7]) were copied in place of the removed element.
And you assign this new slice header (which properly will only include 2 elements) to area1.Cells[1].
But the slice value area2.Cells[1] points to the same backing array, and since you didn't touch this slice value, it still has the length of 3, so it will see all of the backing arrays changed elements: [5, 7, 7].
Also note that your implementation of removeValueFromCell() is faulty, because if the removable element would be listed multiple times in the slice, it would behave incorrectly. The reason for this is because when you remove an element, indices of subsequent elements are shifted (become less by 1), but your loop variable does not account for this. Easiest to handle this is using a downward loop. For details, see How to remove element of struct array in loop in golang.

How slice works in GO?

a = make([]int, 7, 15)
creates implicit array of size 15 and slice(a) creates a shallow copy of implicit array and points to first 7 elements in array.
Consider,
var a []int;
creates a zero length slice that does not point to any implicit array.
a = append(a, 9, 86);
creates new implicit array of length 2 and append values 9 and 86. slice(a) points to that new implicit array, where
len(a) is 2 and cap(a) >= 2
My question:
is this the correct understanding?
As I mentioned "Declare slice or make slice?", the zero value of a slice (nil) acts like a zero-length slice.
So you can append to a []int directly.
You would need to make a slice (make([]int, 0) ) only if you wanted to potentially return an empty slice (instead of nil).
If not, no need to allocate memory before starting appending.
See also "Arrays, slices (and strings): The mechanics of 'append': Nil"
a nil slice is functionally equivalent to a zero-length slice, even though it points to nothing. It has length zero and can be appended to, with allocation.

creating 2 dimensional array of unspecified (varying) size

Newbie question: I want to dynamically create an integer 2D array M[i,j], whose sizes (in both dimensions) are unknown beforehand. Moreover, for each index i, the size of the i-th row may vary.
Question 1: How do I declare such an array (do I even have to)? I have tried Array[], Array(Int64,1...), and Array((Int,Int),0)as in this hint and others.
Question 2: once created, how to I populate the array in a smart and concise way? Say my i-th row is suppose to be equal to a given 1-dimensional B, I would like to write
A[i] = B
or
A[i,:] = B
or even
A[i,1:n] = B
where n is the size of B. All of these give me a BoundsError(). Slicemight do the trick, but I cannot make it agree with my declaration.
You don't want a 2D array here, because in a 2D array all rows are of the same size. Instead, you want a vector-of-vectors. For example:
A = Array(Vector{Int}, 5)
A[1] = rand(1:10, 3)
A[2] = rand(1:100, 22)
If you inspect A, you'll see something like this:
julia> A
5-element Array{Array{Int64,1},1}:
[5,7,7]
[1,63,40,86,61,39,98,5,68,97 … 78,49,44,89,48,63,90,90,86,83]
#undef
#undef
#undef
Another great tool is to use a comprehension:
julia> A = Vector{Int}[ [1:m] for m = 1:5]
5-element Array{Array{Int64,1},1}:
[1]
[1,2]
[1,2,3]
[1,2,3,4]
[1,2,3,4,5]
The main thing you'll want to be careful about is that each element of A is a reference to a vector; if you assign
A[1] = b
A[2] = b
then any change to b will affect both A[1] and A[2]. If you don't want that, use
A[1] = copy(b)
A[2] = copy(b)

Treatment of Arrays in Go

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.

Resources