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.
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
I have already read multiple posts and articles about how ArraySlice works with Array in `Swift.
But, what I couldn't find is how it works internally? What does ArraySlice are Views onto Arrays exactly mean?
var arr = [1, 2, 3, 4, 5]
let slice = arr[2...4]
arr.remove(at: 2)
print(slice.startIndex) //2
print(slice.endIndex) //5
slice[slice.startIndex] //3
In the code above, I've removed element at index-2 (i.e 3) from arr.
Index-2 is the startIndex of slice as well.
When I print slice[slice.startIndex] it still prints 3.
Since no extra storage is created for ArraySlice, then how come any changes in Array doesn't reflect in ArraySlice?
The articles/ posts can be found here:
https://dzone.com/articles/arrayslice-in-swift
https://marcosantadev.com/arrayslice-in-swift/
Both Array and ArraySlice are value types, which means that after
var array = [0, 1, 2, 3, 4, 5]
var slice = array[0..<2]
array and slice are independent values, and mutating one does not affect the other:
print(slice) // [0, 1]
array.remove(at: 0)
print(slice) // [0, 1]
How that is achieved is an implementation detail of the Swift standard library,
but one can inspect the source code to get some ideas: At
Array.swift#L1241
we find the implementation of Array.remove(at:):
public mutating func remove(at index: Int) -> Element {
_precondition(index < endIndex, "Index out of range")
_precondition(index >= startIndex, "Index out of range")
_makeUniqueAndReserveCapacityIfNotUnique()
// ...
}
which uses
#inlinable
#_semantics("array.make_mutable")
internal mutating func _makeUniqueAndReserveCapacityIfNotUnique() {
if _slowPath(!_buffer.isMutableAndUniquelyReferenced()) {
_copyToNewBuffer(oldCount: _buffer.count)
}
}
Following that trail, we find at ArrayBuffer.swift#L107
/// Returns `true` iff this buffer's storage is uniquely-referenced.
#inlinable
internal mutating func isUniquelyReferenced() -> Bool {
// ...
}
This isn't the full implementation yet, but (hopefully) already demonstrates that
the (mutating) remove(at:) method copies the element storage to a new
buffer if is was shared (with another array or an array slice).
We can also verify that by printing the element storage base address:
var array = [0, 1, 2, 3, 4, 5]
var slice = array[0..<2]
array.withUnsafeBytes { print($0.baseAddress!) } // 0x0000000101927190
slice.withUnsafeBytes { print($0.baseAddress!) } // 0x0000000101927190
array.remove(at: 0)
array.withUnsafeBytes { print($0.baseAddress!) } // 0x0000000101b05350
slice.withUnsafeBytes { print($0.baseAddress!) } // 0x0000000101927190
The same “copy-on-write” technique is used if arrays, dictionaries, or strings
are copied, or if String and Substring share storage.
So an array slice shares the element storage with its originating
array as long as neither of them is mutated.
That is still a useful feature. Here is a simple example:
let array = [1, 4, 2]
let diffs = zip(array, array[1...]).map(-)
print(diffs) // [-3, 2]
array[1...] is a view/slice of the given array, without actually copying
the elements.
A recursive binary search function where slices (of the left or right half) are passed down would be another application.
Explanation:
Array slice is a view to the underlying array and retains the array.
Probably (not 100% sure), when you remove an element from the array, it is still retained and is just marked as removed.
That is why when you print the array you don't see 3 but the slice still shows it.
Example:
class Car : CustomStringConvertible {
var description: String {
let objectIdentifier = ObjectIdentifier(self) //To get the memory address
return "car instance - \(objectIdentifier) "
}
deinit {
print("car deallocated")
}
}
var carSlice : ArraySlice<Car>?
var cars : [Car]? = [Car(), Car(), Car()]
carSlice = cars?[0...1]
print("Going to make cars nil")
cars = nil
print("cars = \(cars?.description ?? "nil")")
print("carSlice = \(carSlice?.description ?? "nil")")
print("----------------")
print("Going to make carSlice nil")
carSlice = nil
print("carSlice = \(carSlice?.description ?? "nil")")
Output:
Going to make cars nil
cars = nil
carSlice = [car instance - ObjectIdentifier(0x000060c00001e7b0) , car instance - ObjectIdentifier(0x000060c00001e7c0) ]
----------------
Going to make carSlice nil
carSlice = nil
car deallocated
car deallocated
car deallocated
Apple Documentation:
Long-term storage of ArraySlice instances is discouraged. A slice
holds a reference to the entire storage of a larger array, not just
to the portion it presents, even after the original array’s lifetime
ends. Long-term storage of a slice may therefore prolong the lifetime
of elements that are no longer otherwise accessible, which can appear
to be memory and object leakage.
Refer:
https://developer.apple.com/documentation/swift/arrayslice
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.
I have done this in Objective-C but I can't do what I want to do in Swift.
I am trying to rotate a 2 dimensional array of any Type. I am using generics so that I could Strings & Ints or any other type.
import UIKit
let someArray = [[1,2,3],[7,8,9],[11,93,87]]
print(someArray[0])
func rotateArray<T> (array:[[T]]) ->Array{
var tempArray = [[T]]()
for i in 0..<array.count{
for j in 0..<array.count{
tempArray[j][array.count-i-1] = array[i][j]
}
}
return tempArray
}
someArray.count
let x = rotateArray(someArray)
However I get the following errors ( There could be other errors that I am not aware of), I also read this question and some others but couldn't relate to it.
reference to generic type 'Array' requires arguments in <..>
Binary Operator '..<' Cannot be applied to two 'Int' operands
Edit after fixing the initial two errors: fatal error: Index out of range
What are the things that I am doing wrong? Kindly include details, I am a complete noob.
You have written the return type -> Array, but since Array is generic, you need to specify what it contains, such as Array<Something> or equivalently [Something].
Seemingly, you want to return the same "shape"/type of array as the input, so you can use -> [[T]] for your return type.
(I'm not sure why the compiler produced an error about ..<, but it goes away if you fix the first issue.)
In addition to making your method return type [[T]], you have other problems here. You are instantiating tempArray (the array that will hold the arrays inside), but you are not instantiating those inner arrays. And you can't just use subscript operator, but rather you have to append to your respective arrays.
For example, if you want to rotate clockwise 90 degrees, it would be:
func rotateArray<T> (array:[[T]]) -> [[T]] {
var tempArray = [[T]]()
for column in 0 ..< array.first!.count {
var rowArray = [T]()
for row in (0 ..< array.count).reverse() {
rowArray.append(array[row][column])
}
tempArray.append(rowArray)
}
return tempArray
}
Thus
[[1, 2, 3], [7, 8, 9], [11, 93, 87]]
Becomes
[[11, 7, 1], [93, 8, 2], [87, 9, 3]]
The below code doesn't crash, but I can't reason why, given the "limited" docs available.
func foo(inout a: [Int], inout b: Int) {
a = []
b = 99
}
var arr = [1,2,3]
// confusion: where does the "out" of b go to?
// storage for a was already cleared / invalidated
foo(&arr, b: &arr[2])
print(arr) // arr is empty
I believe this is what is happening. When you assign
a = []
you are pointing a to an new array. The original array still exists in memory and when you do:
b = 99
you are modifying the original array, not the new array that a references.
What evidence do you have that this is the case?
Consider this modification to your experiment:
Case 1:
func foo(inout a: [Int], inout b: Int) {
a[0] = 4
a[1] = 5
a[2] = 6
b = 99
}
var arr = [1,2,3]
foo(&arr, b: &arr[2])
print(arr) // prints "[4, 5, 99]"
Now consider this one:
Case 2:
func foo(inout a: [Int], inout b: Int) {
a = [4, 5, 6]
b = 99
}
var arr = [1,2,3]
foo(&arr, b: &arr[2])
print(arr) // prints "[4, 5, 6]"
Clearly, modifying the individual elements of a is not the same as assigning an array to a.
In Case 1, we modified the original array turning the elements into 4, 5, and 6, and the assignment of b changed a[2] as expected.
In Case 2, we assigned [4, 5, 6] to a which didn't change the original values to 4, 5, and 6 but instead pointed a to a new array. The assignment of b does not change a[2] in this case because a now points to a new array at a different location in memory.
Case 3:
func foo(inout a: [Int], inout b: Int) {
let a1 = a
a = [4, 5, 6]
b = 99
print(a) // prints "[4, 5, 6]"
print(a1) // prints "[1, 2, 99]"
}
var arr = [1,2,3]
foo(&arr, b: &arr[2])
print(arr) // prints "[4, 5, 6]"
In Case 3, we are able to assign the original array to a1 before assigning a new array to a. This gives us a name for the original array. When b is assigned, a1[2] gets modified.
From the comments:
Your answer explains why the assignment to b inside the function
works. However, when foo ends and copies the inout variables back, at
that point I don't see how swift knows to defer deallocating the
original array until after the &[2] assignment.
It is likely that this is a result of reference counting by ARC. The original array is passed by reference to foo and the reference count is incremented. The original array isn't deallocated until the reference count is decremented at the end of foo.
It seems just as hairy as what the docs already disallow - passing the
same variable twice as inout. Also your case3 is surprising. Shouldn't
the let a1 = a line do struct / value semantics and copy a snapshot of
the array right then?
Yes. I agree that Case 3 is surprising, but it does reveal some of what is going on under the covers. Normally when you assign one array to a new variable, Swift does not immediately make a copy. Instead, it just points the second array to the first and the reference count incremented. This is done for efficiency sake. It is only necessary to make a copy when one of the arrays is modified. In this case, when a gets modified, a1 keeps the original copy of the array in memory.
That's really confusing; I don't see why a1 wouldn't get 1,2,3. Also a
let should be immutable!
The fact that a1 gets modified when b is set shows that a1 is pointing to the memory of the original array. Swift apparently doesn't consider setting b as modifying a1. Perhaps because it already made sure a was mutable when foo was called with &a[2].