Swift: inout with generic functions and constraints - swift

I am making my first steps in Swift and got along the first problem. I am trying to pass an array by reference using inout on an generic function with constraints.
First, my application start point:
import Foundation
let sort = Sort()
sort.sort(["A", "B", "C", "D"])
And here my class with the actual problem:
import Foundation
class Sort {
func sort<T:Comparable>(items:[T]){
let startIndex = 0
let minIndex = 1
exchange(&items, firstIndex: startIndex, secondIndex: minIndex)
}
func exchange<T:Comparable>(inout array:[T], firstIndex:Int, secondIndex:Int) {
// do something with the array
}
}
I am getting the following error in Xcode on the line calling exchange:
Cannot convert value of type '[T]' to expected argument type '[_]'
Am I missing here something?
Update: Added complete project code.

It works with following modifications:
the array passed into it must be a var. As mentioned in the documentation, inouts must not be let or literals.
You cannot pass a constant or a literal value as the argument, because constants and literals cannot be modified.
items in declaration must also be inout to indicate that it must be var again
import Foundation
class Sort {
func sort<T:Comparable>(inout items:[T]){
let startIndex = 0
let minIndex = 1
exchange(&items, firstIndex: startIndex, secondIndex: minIndex)
}
func exchange<T:Comparable>(inout array:[T], firstIndex:Int, secondIndex:Int) {
// do something with the array
}
}
let sort = Sort()
var array = ["A", "B", "C", "D"]
sort.sort(&array)

You can "exchange" two values in an array by using the swift swap function.
e.g.
var a = [1, 2, 3, 4, 5]
swap(&a[0], &a[1])
would mean that a now is [2, 1, 3, 4, 5]

Related

Is it possible to make array reference immutable, but array content mutable?

In Java, we can make an array reference immutable, and array content mutable, by using final keyword
Java
final int[] array = {1, 2, 3};
// Ok. Array content mutable.
array[0] = 9;
// Compiler error. Array reference immutable.
array = new int[]{4, 5, 6};
In Swift, they take one step further. Using let keyword, will make both array reference, and array content immutable.
Swift
let array = [1, 2, 3]
// Compiler error. Array content immutable.
array[0] = 9
// Compiler error. Array reference immutable.
array = [4, 5, 6]
In Swift, is it possible to make array reference immutable, but array content mutable?
The answer for your question is "yes" and "no", depends on what you have.
If you decide to declare a simple "let" constant, you can't modify it.
Why ? Because it prevents you to side effects (and you have some optimization).
For example if you just want to browse a list and print values, you don't modify the list.
myArray = [1,2,3]
for element in myArray {
print(element)
}
Why it can be cool ? Now if you know that you don't want to modify your list, it prevents you to use functions that can modify your list. It will save your time and avoid some behavior that you don't expect.
If you declare a var and you don't modify the value, Swift will tell you too.
Moreover, the concept of immutable in Swift is interesting if you use a struct or a class.
Imagine you have this structure and this class:
struct TestStruct {
var myInt: Int
init(myInt: Int) {
self.myInt = myInt
}
}
struct TestClass {
var myInt: Int
init(myInt: Int) {
self.myInt = myInt
}
}
In this structure you have myIntwhich is a var. What happens if you try to declare a TestStructure and a TestClass object with a let constant ?
let testStruct = Test(myInt: 3)
// Cannot assign to property: 'test' is a 'let' constant
test.myInt = 5
let testClass = Test(myInt: 3)
// It works
test.myInt = 5
In a struct, the let is propagated for every field, which is not the case for a class.
Using let keyword, will make both array reference, and array content immutable.
This isn't correct. There is no "array reference" here. An array is a value, just like an integer is a value. There is no "array reference." Variables can be let or var, but that doesn't change the nature of their value. You wouldn't say that var n = 4 made "4" mutable. Similarly, var ns = [1,2,3] doesn't make [1,2,3] mutable. It just means you can change what ns refers to. Calling ns.append(5) is just like n += 1. In each case they assign a new value. They don't mutate the old value.
As an implementation and optimization detail, it is possible that the underlying array storage that was used for ns will be mutated and used for the new ns value. But this is invisible to the caller. For example:
var array = [1,2] {
didSet { print("\(oldValue) -> \(array)") }
}
array.append(1)
array = [1,2,1]
// [1, 2] -> [1, 2, 1]
// [1, 2, 1] -> [1, 2, 1]
There's no deep difference between the append and the assignment. They are both assignments. And notice that setting the value to the same value is still just an assignment.
I'm harping on this because you can't just translate over a Java approach and have it work if your Java code relies on shared mutable state (where one part of the program modifies an array and others are supposed to have their reference update). But if your Java works that way, I recommend improving your Java to reduce its reliance on that. As long as you generally just pass values and return values, then it'll work exactly the same in Swift as in Java.
If you still need this kind of mutable array, then you can build one fairly easily by wrapping an Array in a class:
final class ArrayRef<Element>: MutableCollection, ExpressibleByArrayLiteral {
private var elements: [Element] = []
init(arrayLiteral elements: Element...) {
self.elements = elements
}
var startIndex: Int { elements.startIndex }
var endIndex: Int { elements.endIndex }
func index(after i: Int) -> Int { elements.index(after: i) }
subscript(position: Int) -> Element {
get { elements[position] }
set { elements[position] = newValue }
}
}
let array: ArrayRef = [1, 2, 3]
// Ok. "Array" content mutable.
array[0] = 9
// Compiler error. "Array" is immutable.
array = [4, 5, 6]
(This is a very simple and unoptimized implementation. With more work you can make it more efficient and improve the interface.)
But I don't particularly recommend this unless you really need it. There's a reason it doesn't exist in stdlib.

A Constant Array's Items Can(Cannot) Be Modified If They Are Reference(Value) Types. Why?

Please have a look at the following code and note the compiler error on the last line:
class C {
var value: Int
init( _ value: Int) { self.value = value }
}
let array1 = [C(1), C(2), C(3)]
array1[0].value = 4
struct S {
var value: Int
}
let array2 = [S(value: 1), S(value: 2), S(value: 3)]
array2[0].value = 4 // Error: Cannot assign to property: 'array2' is a 'let' constant
From the compiler error I want to conclude that the item at index 0 is being read from array2, modified, and then written back to array2. What else could produce the result that there is an attempt to modify array2? But, if my reasoning is correct, then why does the same thing not happen with array1?
Classes Are Reference Types
Unlike value types(struct), reference types are not copied when they are assigned to a variable or constant, or when they are passed to a function. Rather than a copy, a reference to the same existing instance is used instead.
Please refer Apple documentation about Class & struct

Creating an UnsafeMutablePointer<UnsafeMutablePointer<Float>> parameter in Swift 3

The Swift signature of the Accelerate framework vDSP_biquadm() function includes parameter types of UnsafeMutablePointer<UnsafePointer<Float>> and UnsafeMutablePointer<UnsafeMutablePointer<Float>>.
How does one declare and create such types in Swift 3, and then fill these pointer arrays with references to several Swift arrays of type [Float].
When you need to pass multiple values for UnsafeMutablePointer<T> parameters, you need to declare a variable of type [T] (aka Array<T>) and pass it as an inout argument. In your case T is UnsafeMutablePointer<Float>.
So, if you want to start with [Float], you may need to write something like this:
let input: [Float] = [/*...*/]
var output: [Float] = Array(repeating: 0, count: outputTotalSize)
input.withUnsafeBufferPointer {inBuf in
let inputPtr = inBuf.baseAddress!
output.withUnsafeMutableBufferPointer {outBuf in
let outputPtr = outBuf.baseAddress!
var pInputs: [UnsafePointer<Float>] = [inputPtr,/*...*/]
var pOutputs: [UnsafeMutablePointer<Float>] = [outputPtr/*...*/]
vDSP_biquadm(setup, &pInputs, inStride, &pOutputs, outStride, length)
}
}

Chaining methods in Swift

I'd like to write a function that I can chain to map { } functions. For example:
let arr = [1,2,3,4,5]
let list = arr.map { $0 * 2 }.applyRuleToSubtractVal(1) // return [1,3,5,7,9]
How do I define applyRuleToSubtractVal() above? (I want to do more complex things and don't want to do them inside the map.)
sketchyTech's answer is correct, but actually it is just hiding the fact, that
let list = arr.map { $0 * 2 }.map { $0 - 1 }
is executed — this is not optimal as the array is enumerated twice.
You could achieve the same with one enumeration with
let complexClosure = { (i: Int) in
return i-1
}
let list = arr.map { complexClosure($0 * 2) }
Your map returns an array of Int so you can apply any Array method that can be performed upon an Array<Int> using dot syntax. In order to include your own methods make sure that you extend Array in a way that makes this possible, e.g.
extension Array where Element:IntegerType {
func applyRuleToSubtractValue(num:Int) -> Array {
return self.map{$0 - 1}
}
}
let arr = [1,2,3,4,5]
let list = arr.map{$0 * 2}.applyRuleToSubtractValue(1) // [1, 3, 5, 7, 9]
If we didn't use protocol restrictions here then we wouldn't be able to perform a minus 1 operation, because it might be an array of any type.
You need to extend the Array's functionality. The dot operator calls methods of the class or struct. Extend your own functionality to the Array struct.

Optional vs Bound value assigning var from array

I want to check if there is a value in a array and if so assign to a String using a if-left statement:
if let scoreValue = scoreValueArray[element!]{
// do something with scoreValue
}
Error: Bound value in a conditional binding must be of optional type
So tried changing the ! to ? but error persists.
Any input appreciated.
scoreValueArray is an array of strings, where a String value is appended to array if a condition is met, then array is saved to NSUserdefaults.
So element is a int which corresponds to a index in the array, bt only if the index is occupied with a String, so
scoreValueArray[element!]
could return an 'Index out of bounds', hence want to use the if-let.
Although the accepted answer clearly puts why optional binding is not available in the current implementation, it doesn't provide with a solution.
As it is shown in this answer, protocols provide an elegant way of safely checking the bounds of an array. Here's the Swift 2.0 version:
extension Array {
subscript (safe index: Int) -> Element? {
return indices ~= index ? self[index] : nil
}
}
Which you can use like this:
let fruits = ["Apple", "Banana", "Cherry"]
if let fruit = fruits[safe: 4] {
// Do something with the fruit
}
It's not clear what type your scoreValueArray is, but for the sake of this answer, I'm going to assume it's an array of Int.
var scoreValueArray: Array<Int>
Now, if we look the definition of the Array struct, we'll find this:
struct Array<T> : MutableCollectionType, Sliceable {
// other stuff...
subscript (index: Int) -> T
// more stuff
}
So, calling the subscript method on our array (which is what we do when we say scoreValueArray) returns a non-optional. And non-optionals cannot be used in the conditional binding if let/if var statements.
We can duplicate this error message in a more simple example:
let foo: Int = 3
if let bar = foo {
// same error
}
This produces the same error. If we instead do something more like the following, we can avoid the error:
let foo: Int? = 3
if let bar = foo {
// perfectly valid
}
This is different from a dictionary, whose subscript method does return an optional (T?). A dictionary will return a value if the key passed in the subscript is found or nil if there is no value for the passed key.
We must avoid array-index-out-of-bounds exceptions in the same way we always do... by checking the array's length:
if element < scoreValueArray.count {
scoreValue = scoreValueArray[element]
}