Multiple errors with using multidimensional array of generics - arrays

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]]

Related

How does Rust determine the length of a slice when unsizing from an array?

I know that a slice is like a fatptr: (data_ptr, len). When I slice an array into a slice:
let arr = [0; 10];
let slice_arr = &arr[..];
An array doesn't have a length field like Vec<T>.
I know that slice_arr.len() can get the length of slice because slice_arr has a length field. How does Rust know the length field when an array is converted into a slice?
An array does have a length "parameter" of sorts. It's not a field, it's part of the type:
let x: [u8; 2] = [1, 2];
let y: [u8; 3] = x; // Error here
The type of x is [u8; 2]. The number of elements is always 2, and the type of the elements is always u8.
Because [u8; 2] and [u8; 3] are distinct types, one is always exactly 2 u8s long, and the other is always exactly 3 u8s long, the assignment from x into y fails.
When performing certain operations with arrays, the compiler has special built-in semantics for the arrays. These built-in semantics can make use of the length "parameter" (The N in [T; N]). You can manipulate and access this value at the type level using const generics, but that's a nightly-only feature right now.
Use the .len method.
use std::io::stdin;
fn main() {
let arr = [0; 10];
let slice_arr = &arr[..];
println!("first element of the slice: {}", slice_arr[0]);
println!("the slice has {} elements", slice_arr.len());
}
https://doc.rust-lang.org/stable/rust-by-example/primitives/array.html

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 ArraySlice in Swift work internally?

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

Remove duplicates from a multi-dimensional array

I wrote the following extension to remove duplicates from my Array.
extension Array where Element : Equatable{
func removeDups() -> [Element]{
var result = [Element]()
for element in self{
if !result.contains(element){
result.append(element)
}
}
return result
}
}
Linear Array
let linearArr = [1,2,2,3]
linearArr.removeDups() // [1,2,3] works well!
Multi dimensional Array
let multiDimArr : [[Int]] = [[1,2,3], [1,2,3], [1,2 ,4]]
multiDimArr.removeDups() // Error!
Type [Int] does not conform to Equatable
I read here. And the answer says Array comparisons using == should work. It doesn't work all the time:
Works
if (["1", "2"] == ["1", "2"]){
print("true")
}
Doesn't work
if ([1, 2] == [1, 2]){ // ERROR!
print("true")
}
Ambiguous use of operator '=='
This is peculiar. I can compare Array of Strings but can't compare Array of Ints.
I also saw this comment:
The reason myArray1 == myArray2 is that NSObject conforms to Equatable, calling -[equals:] to conduct the test
Not sure if the ☝️ comment is still valid.
To summarize:
Are arrays equatable? Can I compare them using ==
Why is it different for comparing Array of Strings vs. Array of Ints
How can I remove duplicates from a multi-dimensional array?
I'm currently working with Swift 4.0.2
Are arrays equatable? Can I compare them using ==
Prior to Swift 4.1, Array didn't conform Equatable. There was however an overload of == that compared two arrays with Equatable elements, which is what enabled this to compile:
if ["1", "2"] == ["1", "2"] { // using <T : Equatable>(lhs: [T], rhs: [T]) -> Bool
print("true")
}
However in Swift 4.1 (available with Xcode 9.3), Array<Element> now conforms to Equatable when its Element conforms to Equatable. This change is given in the changelog:
Swift 4.1
[...]
SE-0143 The standard library types Optional, Array, ArraySlice, ContiguousArray, and Dictionary now conform to the Equatable protocol when their element types conform to Equatable. This allows the == operator to compose (e.g., one can compare two values of type [Int : [Int?]?] with ==), as well as use various algorithms defined for Equatable element types, such as index(of:).
Your example with multiDimArr.removeDups() compiles and runs as expected in 4.1, yielding the result [[1, 2, 3], [1, 2, 4]].
In Swift 4.0.3, you could hack it by adding another overload of removeDups() for nested arrays:
extension Array {
func removeDups<T : Equatable>() -> [Element] where Element == [T] {
var result = [Element]()
for element in self{
if !result.contains(where: { element == $0 }) {
result.append(element)
}
}
return result
}
}
let multiDimArr = [[1, 2, 3], [1, 2, 3], [1, 2, 4]]
print(multiDimArr.removeDups()) // [[1, 2, 3], [1, 2, 4]]
This does unfortunately lead to some code duplication, but at least you'll be able to get rid of it when updating to 4.1.
The fact that this example doesn't compile in either 4.0.3 or 4.1:
if [1, 2] == [1, 2] { // error: Ambiguous use of operator '=='
print("true")
}
is due to the bug SR-5944 – the compiler is considering it to be ambiguous due to == overloads for IndexSet and IndexPath (both of which are ExpressibleByArrayLiteral). But Swift should default an array literal to Array though, resolving the ambiguity.
Saying either:
if [1, 2] as [Int] == [1, 2] {
print("true")
}
or not importing Foundation resolves the issue.
Finally, it's worth noting that the performance of removeDups() can be improved if the Element type is also Hashable, allowing it to run in linear, rather than quadratic time:
extension Array where Element : Hashable {
func removeDups() -> [Element] {
var uniquedElements = Set<Element>()
return filter { uniquedElements.insert($0).inserted }
}
}
Here we're using a set to store the elements that we've seen, omitting any that we've already inserted into it. This also allows us to use filter(_:), as #Alexander points out.
And in Swift 4.2, Array also conditionally conforms to Hashable when its Element is Hashable:
Swift 4.2
[...]
SE-0143 The standard library types Optional, Array, ArraySlice, ContiguousArray, Dictionary, DictionaryLiteral, Range, and ClosedRange now conform to the Hashable protocol when their element or bound types (as the case may be) conform to Hashable. This makes synthesized Hashable implementations available for types that include stored properties of these types.
This is a place where recursion would solve the problem. Have you considered recursion? I was going to answer the question with actual code, but I don't know the syntax for Swift. so, here is some pesudocode:
function removeDupes() {
buffer = array;
foreach this->elements as key => element {
if(element is type array) {
this->elements[key] = element->removeDupes();
} else {
if(!this->contains(element)) {
buffer->add(element);
}
}
}
return buffer;
}
Basically, you want to test if the element itself is an array. If it is, then you want to call that array's removeDupes() method (which in turn will look for duplicates, unless it finds another array, then it will call itself again).

Transforming RandomAccessSlice to RandomAccessCollection

When we try to retrieve a range of elements from an Array, we get back an ArraySlice:
let array = [1, 3, 5, 2]
let arraySlice = array[..<2] // elements up to index 1 == [1, 3]
We can transform it back to the Array type like so:
let arrayFromSlice = Array(arraySlice)
Let's say you want to create a method that returns the first 3 elements of any RandomAccessCollection:
func first3Elements<T: RandomAccessCollection>(_ c: T) -> T {
let slice = c.prefix(3)
// COMPILER ERROR: non-nominal type 'T'
// does not support explicit initialization
return T(slice)
}
Is it possible to perform this conversion?
Here my first attempt using type erasure but I guess there are better solutions.
func first3Elements<T>(_ c: AnyRandomAccessCollection<T>) -> AnyRandomAccessCollection<T> {
let slice = c.prefix(3)
return AnyRandomAccessCollection(slice)
}
let array = AnyRandomAccessCollection([1, 2, 3, 4])
let result = first3Elements(array)
for x in result {
print(x)
}

Resources