Swift array reference and copy [duplicate] - arrays

I am trying to reference to an array inside a function.
Something like this: a and b are arrays of Ints.
var inout refArr = &a
if(!someFlag) {
refArr = &b
}
refArr[someIndex] = 30
This does not compile, can I only use inout for function arguments?
If so, how do I do a reference/pointer inside a function?

& can only be used to pass a variable as an inout argument to a function. So the easiest solution is perhaps to use a helper function
inside your function:
func foo() {
func helper(inout array : [Int]) {
array[2] = 99
}
var a = [1, 2, 3, 5, 6]
var b = [4, 5, 6, 7]
let someFlag = true
if someFlag {
helper(&a)
} else {
helper(&b)
}
// ...
}
You can create a reference to the array using UnsafeMutableBufferPointer:
let ref = someFlag ?
UnsafeMutableBufferPointer(start: &a, count: a.count) :
UnsafeMutableBufferPointer(start: &b, count: b.count)
ref[2] = 99
But there are two problems with this solution:
UnsafeMutableBufferPointer() creates a non-owning reference,
so the compiler might decide to deallocate the array while the reference
is still used.
There is no bounds check on the array.
So to make this work safely, you have to add some code:
withExtendedLifetime(a) { () -> Void in
withExtendedLifetime(b) { () -> Void in
let ref = someFlag ?
UnsafeMutableBufferPointer(start: &a, count: a.count) :
UnsafeMutableBufferPointer(start: &b, count: b.count)
if ref.count > 2 {
ref[2] = 99
}
}
}
which is a bit ugly.

You can use inout parameters in your function to accomplish this. Use the inout modifier for your parameters and use the ampersand (&) when passing a value into the function like so:
func swapTwoInts(inout a: Int, inout b: Int) {
let temporaryA = a
a = b
b = temporaryA
}
var x = 5
var y = 10
swapTwoInts(&x, &y)
x // 10
y // 5

Arrays in swift are not guaranteed to be contiguous in memory the way they are in C, so to insure you have access to a contiguous block, you have to either pass them to a function with an inout parameter or--if you really want a reference to an array within the function--create an UnsafeMutableBufferPointer like this[1]:
var someFlag: Bool = false
var a = ["a", "b", "c"]
var b = ["d", "e", "f"]
var refArr = a.withUnsafeMutableBufferPointer { (inout output: UnsafeMutableBufferPointer<String>) -> UnsafeMutableBufferPointer<String> in
return output
}
println(refArr[1]) //Will Output 'a'
if !someFlag {
refArr = b.withUnsafeMutableBufferPointer { (inout output: UnsafeMutableBufferPointer<String>) -> UnsafeMutableBufferPointer<String> in
return output
}
}
println(refArr[1]) //Will Output 'e'
1. Thread Safety with Swift Arrays

Related

Iterating through references to variables in Swift

I am looking for a way to change the values of multiple variables using iteration in Swift. An example would be something like this:
var a = false
var b = false
var c = false
func makeAllTrue() {
for n in [a, b, c] {
n = true
}
}
...but rather than an array of values, I want to iterate through an array of pointers/references to the variables above.
Any advice would be greatly appreciated.
var a = false
var b = false
var c = false
mutateValues(&a, &b, &c) { n in
n = true
}
print(a, b, c) // will be printed "true true true"
func mutateValues<Value>(_ values: UnsafeMutablePointer<Value>..., mutate: (inout Value) -> Void) {
values.forEach {
mutate(&$0.pointee)
}
}
It is possible to do this with key paths. Let's say the properties are in a class Foo:
class Foo {
var a = false
var b = false
var c = false
func makeAllTrue() {
for n in [\Foo.a, \Foo.b, \Foo.c] {
self[keyPath: n] = true
}
}
}
If Foo is a struct, use mutating func instead:
struct Foo {
var a = false
var b = false
var c = false
mutating func makeAllTrue() {
for n in [\Foo.a, \Foo.b, \Foo.c] {
self[keyPath: n] = true
}
}
}
However, if the class name is long, I don't think it is worth doing this way.
If these three properties are very related, I would not bother with the key path stuff and replace a, b and c with an array:
var abc = [false, false, false]
and have the for loop loop over the indices:
for i in abc.indices {
abc[i] = true
}
An Array in Swift is a struct, hence a value type.
Iterating over his children, and changing one, will not be possible unless:
The type of child is aa class (which is reference typed)
You iterate over the indices and change the real values!
E.G:
var a: Int = 1
var b: Int = 2
var array: [Int] = [a,b]
for index in array.indices {
array[index] += 1
}
print(array) // [2,3]

Shallow copy an array/modify arrays based on condition in swift

var a=[0,0,0,0]
var b=[0,0,0,0]
for i in 0..<4{
var g=i%2==0 ? a:b
g[i]+=1
//10 more lines of code about array g
}
I want to implement something like whenever i is an even number increment the i position of A by 1, and if i is odd increment the i position of B by 1.
The expecting result is A=[1,0,1,0] and B is [0,1,0,1]
Here modifying the array g will not affect the original array because of deep copy. Are there any ways to modify array a and b without using if...else statement?
You have to modify a value type directly, a possible solution is
var a = [0,0,0,0]
var b = [0,0,0,0]
for i in 0..<4 {
i.isMultiple(of: 2) ? (a[i] += 1) : (b[i] += 1)
}
Another solution is to use a class (reference type) wrapper
class Wrapper {
var array = [0,0,0,0]
}
let a = Wrapper()
let b = Wrapper()
for i in 0..<4 {
let g = i.isMultiple(of: 2) ? a : b
g.array[i] += 1
}
print(a.array)
print(b.array)
var a=[0,0,0,0]
var b=[0,0,0,0]
for i in 0..<4{
i%2==0 ? (a[i] += 1) : (b[i] += 1)
}
print(a)
print(b)
Result A=[1,0,1,0] B = [0,1,0,1]
You could use a function with an inout parameter:
func modify(g: inout [Int], index: Int) {
g[index] += 1
// 10 more lines of code about array g
}
var a = [0,0,0,0]
var b = [0,0,0,0]
for i in 0..<4 {
if i.isMultiple(of: 2) {
modify(g: &a, index: i)
} else {
modify(g: &b, index: i)
}
}
print(a)
print(b)
You could also put the arrays into a class (reference type) and use that reference to access the array instead of trying to reference the arrays (value types) directly.
See inout documentation here
Some info on value vs reference types
You are not "thinking value type", yet. For those, copy, modify, and reassign.
var array: [Int] { .init(repeating: 0, count: 4) }
let (a, b) = array.indices.reduce( into: (array, array) ) { arrays, index in
let indexIsEven = index.isMultiple(of: 2)
// Copy
var array = indexIsEven ? arrays.0 : arrays.1
// Modify
array[index] = 1
// Reassign
indexIsEven
? (arrays.0 = array)
: (arrays.1 = array)
}

Reference to array inside function in Swift

I am trying to reference to an array inside a function.
Something like this: a and b are arrays of Ints.
var inout refArr = &a
if(!someFlag) {
refArr = &b
}
refArr[someIndex] = 30
This does not compile, can I only use inout for function arguments?
If so, how do I do a reference/pointer inside a function?
& can only be used to pass a variable as an inout argument to a function. So the easiest solution is perhaps to use a helper function
inside your function:
func foo() {
func helper(inout array : [Int]) {
array[2] = 99
}
var a = [1, 2, 3, 5, 6]
var b = [4, 5, 6, 7]
let someFlag = true
if someFlag {
helper(&a)
} else {
helper(&b)
}
// ...
}
You can create a reference to the array using UnsafeMutableBufferPointer:
let ref = someFlag ?
UnsafeMutableBufferPointer(start: &a, count: a.count) :
UnsafeMutableBufferPointer(start: &b, count: b.count)
ref[2] = 99
But there are two problems with this solution:
UnsafeMutableBufferPointer() creates a non-owning reference,
so the compiler might decide to deallocate the array while the reference
is still used.
There is no bounds check on the array.
So to make this work safely, you have to add some code:
withExtendedLifetime(a) { () -> Void in
withExtendedLifetime(b) { () -> Void in
let ref = someFlag ?
UnsafeMutableBufferPointer(start: &a, count: a.count) :
UnsafeMutableBufferPointer(start: &b, count: b.count)
if ref.count > 2 {
ref[2] = 99
}
}
}
which is a bit ugly.
You can use inout parameters in your function to accomplish this. Use the inout modifier for your parameters and use the ampersand (&) when passing a value into the function like so:
func swapTwoInts(inout a: Int, inout b: Int) {
let temporaryA = a
a = b
b = temporaryA
}
var x = 5
var y = 10
swapTwoInts(&x, &y)
x // 10
y // 5
Arrays in swift are not guaranteed to be contiguous in memory the way they are in C, so to insure you have access to a contiguous block, you have to either pass them to a function with an inout parameter or--if you really want a reference to an array within the function--create an UnsafeMutableBufferPointer like this[1]:
var someFlag: Bool = false
var a = ["a", "b", "c"]
var b = ["d", "e", "f"]
var refArr = a.withUnsafeMutableBufferPointer { (inout output: UnsafeMutableBufferPointer<String>) -> UnsafeMutableBufferPointer<String> in
return output
}
println(refArr[1]) //Will Output 'a'
if !someFlag {
refArr = b.withUnsafeMutableBufferPointer { (inout output: UnsafeMutableBufferPointer<String>) -> UnsafeMutableBufferPointer<String> in
return output
}
}
println(refArr[1]) //Will Output 'e'
1. Thread Safety with Swift Arrays

How to return an inout (reference) in a swift function?

I'd like a function to return a reference of an array:
var a = [1, 2]
var b = [3, 4]
func arrayToPick(i:Int) -> [Int] {
return i == 0 ? a : b
}
inout var d = arrayToPick(0)
d[0] = 6
println(a[0]) // 1
println(d[0]) // 6
I'm unable to return &a or &b in arrayToPick because those can't be casted to [Int].
How to return a reference on a or b from a function?
You cannot return inout value. Because the compiler cannot guarantee the lifetime of the value.
You have unsafe way, like this:
var a = [1, 2]
var b = [3, 4]
func arrayToPick(i:Int) -> UnsafeMutablePointer<[Int]> {
if i == 0 {
return withUnsafeMutablePointer(&a, { $0 })
}
else {
return withUnsafeMutablePointer(&b, { $0 })
}
}
var d = arrayToPick(0)
d.memory[0] = 6
println(a[0]) // -> 6
In this case, after a is deallocated, d.memory access may cause BAD_ACCESS error.
Or safe way, like this:
var a = [1, 2]
var b = [3, 4]
func withPickedArray(i:Int, f:(inout [Int]) -> Void) {
i == 0 ? f(&a) : f(&b)
}
withPickedArray(0) { (inout picked:[Int]) in
picked[0] = 6
}
println(a[0]) // -> 6
In this case, you can access the picked value only in the closure.

Accessing element of an array with iterator instead of position in Swift

I would like to know if there are iterators for array in Swift, like in CPP, which permits to avoid the using of position to access to the next element of an array.
Actually, I use position + 1 in my array to access its next element:
var array = ["a", "b", "c", "d"]
func arrayToString(success : String -> Void)
{
var str = String()
var myFunc : (Int -> Void)!
myFunc = {
if $0 < array.count
{
str += array[$0]
myFunc($0 + 1)
}
else
{
success(str)
}
}
myFunc(0)
}
arrayToString({
println($0)
})
I'm looking for a solution usable like this:
var array = ["a", "b", "c", "d"]
func arrayToString(success : String -> Void)
{
var str = String()
var myFunc : (Iterator -> Void)!
myFunc = {
if $0 != nil
{
str += array[$0]
myFunc($0.next)
}
else
{
success(str)
}
}
myFunc(array.first)
}
arrayToString({
println($0)
})
Any suggestions ?
What you want is to use a generator:
var array = ["a", "b", "c", "d"]
var g = array.generate()
var str = String()
for (var i = g.next(); i != nil; i = g.next()) {
str += i!
}
This is very handy when you want to parse command line arguments:
var args = Process.arguments.generate()
while let arg = args.next() {
if (arg == "--output") {
let output = args.next()!
...
}
}
In Swift 3, this now becomes:
var args = Process.arguments.makeIterator()
while let arg = args.next() {
if (arg == "--output") {
let output = args.next()!
...
}
}
- I can think of a work around which can be implemented here, and its the use of map function of Array.
Iteration over each Elements:
let arr = [1, 2, 3, 4, 5]
arr.map { a in print("\(a + 1) ") }
Output:
2 3 4 5 6
- So in this way you can achieve the iteration over each element of the Array in Swift.

Resources