Confused about how to use mutating - arrays

I have a simple struct called Card...
struct Card {
var order: Int? = -1
var data : [String: String]
var original : String?
And a collection object called Deck that looks like...
struct Deck {
var cards : [Card]
Deck has the reading and writing methods, which basically boils down to splitting up strings read in from a text file, and then pushing it bit by bit into the previous empty data. Here's an example...
mutating func parseGRCard(var c: Card) {
c.data["I1"] = c.original![2...4].trim()
c.data["I2"] = c.original![5...9].trim()
}
To read the file, I get each line, make a Card, and then call the parse methods on it...
let nc = Card(order: i, original: c)
parseGRCard(nc)
cards.append(nc)
When I step through this func, I see that mc's original has the expected data, the original line from the text file. I then watch parseGRCard read it and add the items to data, which now has two items. But when when it returns and nc is appended, data is empty.
I thought mutating was supposed to handle these things, but apparently I'm missing something fundamental here.

I changed your code a bit to make it compile and make it more illustrative.
append is mutating because it mutates cards.
It's parameter c is an inout parameter so that the passed Card is passed back after the function. Since Structs are value types and not reference types a new copy is actually passed to the function. This behaviour has nothing to do with the function being a mutating function.
struct Card {
var placeInDeck: Int = 0
}
struct Deck {
var cards : [Card] = []
mutating func append(inout c: Card) {
c.placeInDeck = cards.count
cards.append(c)
}
}
var cardZero = Card()
var cardOne = Card()
var deck = Deck()
deck.append(&cardZero)
deck.append(&cardOne)
cardZero.placeInDeck // 0
cardOne.placeInDeck // 1
In this case the function is not mutating because no properties from Deck are altered. The c parameter is a variable which just makes it mutable inside the scope from the function. Beware, this will be removed from Swift. When the function ends, this mutable copy of Card will not update it's original instance outside the scope of the function. The updated Card will not persist.
struct Card {
var placeInDeck: Int = 0
}
struct Deck {
var cards : [Card] = []
func updateCount(var c: Card) {
c.placeInDeck = 1
}
}
var cardZero = Card()
var cardOne = Card()
var deck = Deck()
deck.updateCount(cardZero)
deck.updateCount(cardOne)
cardZero.placeInDeck // 0
cardOne.placeInDeck // 0

From Apple's documentation methods, we can read the following regarding the mutable keyword:
However, if you need to modify the properties of your structure or
enumeration within a particular method, you can opt in to mutating
behavior for that method. The method can then mutate (that is, change)
its properties from within the method, and any changes that it makes
are written back to the original structure when the method ends.
I agree that it is could be possible to interpret the bold-marked part as as "the properties of the method, i.e., it's parameters?". From your example above, it would seem as if you have done this interpretation.
But the mutable keyword only tells us that the associated function is allowed to change (mutate) variable member values of the struct (in this case) that owns the method.
struct SingleIntegerValueStruct1 {
var myInt = 1
func LetsTryToMutateTheInteger () {
myInt += 1 // compile time error; myInt immutable
}
}
Whereas if we use the mutating keyword
struct SingleIntegerValueStruct {
var myInt = 0
mutating func LetsTryToMutateTheInteger () {
myInt += 1 // ok, mutating function
}
}
var a = SingleIntegerValueStruct(myInt: 1)
print("\(a.myInt)") // 1
a.LetsTryToMutateTheInteger()
print("\(a.myInt)") // 2
However, a struct type is and will always be a value type. So when when a struct type is passed to any function, the function will not be able to mutate the caller parameter, as it is only given a copy of it.
let nc = Card(order: i, original: c)
parseGRCard(nc) // passes _copy_ of struct typ nc to parseGRCard
cards.append(nc) // nc in this scope is stil unchanged
For wanting to use a "stand-alone" function that could mutate its input parameter, where the input is a struct, you could, as discussed in the comments, us the inout keyword on the function parameter.
We extend our example from above to include such a case:
struct SingleIntegerValueStruct {
var myInt = 0
mutating func LetsTryToMutateTheInteger () {
myInt += 1 // ok, mutating function
}
}
// "b" here is a _copy_ of "a" (in parameter), and the function
// thereafter returns a _copy_ of "b" (with state of "b" as its
// final state in the function)
func standAloneInOutFunction(inout b: SingleIntegerValueStruct) {
b.LetsTryToMutateTheInteger()
}
var a = SingleIntegerValueStruct(myInt: 1)
print("\(a.myInt)") // 1
a.LetsTryToMutateTheInteger()
print("\(a.myInt)") // 2
standAloneInOutFunction(&a)
// value copy a->b, modify(b), value copy back b->a
print("\(a.myInt)") // 3
Note however that the inout keyword does not mean that we pass by reference (as for class instances), but we simply send a value copy in and takes another value copy back, that we finally assigns to the original value type calling parameter (a).
From the Apple documentation on this keyword:
... An in-out parameter has a value that is passed in to the function,
is modified by the function, and is passed back out of the function to
replace the original value.

Related

Swift Struct instance get changed on updating it's value

I have observed that when we do change a property value of struct, then a new struct object is created.
struct StructureTest {
var i: Int
mutating func changeValue(_ val: Int) {
i = val
}
}
var st = StructureTest(i: 10) {
didSet{
print("struct changed")
}
}
print("before: \(st)")
st.changeValue(20)
print("after: \(st)")
Output:
before: StructureTest(i: 10)
struct changed
after: StructureTest(i: 20)
Main thing I have noticed that after changing it's property value. A new struct object is created and assigned to var st.
I do know what are "value types" and what is "copy on assign" feature.
But I am unable to understand that why is it happening here?
Maybe I am missing something here?
Let me know if you do know the reason for this new struct instance creation.
Along side with this I have observer one more thing that:
If I have a array of structs. Like as:
struct StructureTest {
var i: Int
mutating func changeValue(_ val: Int) {
i = val
}
}
var arrStructs = [StructureTest(i: 10), StructureTest(i: 20)] {
didSet {
print("arrStructs changed")
}
}
arrStructs[0].changeValue(30)
Output:
arrStructs changed
I am unable to understand that why is array modifying?
As much I can understand from "copy of write" feature on value types.
It should happen when an array is modified and array capacity requires to create a new array but in this case array modification reason is not clear to me.
Do let me know If you do know the reason behind that or if you can provide me any reference for clarification.
Sorry for my grammatical mistakes. Hope the essence of problem is clear to you.
The structs have value semantics, so when you mutate a struct you have assigned a new value to it; ie you changed the value that the variable holds. Array in swift is a generic struct and it therefore also has value semantics (unlike most languages where array is a reference). Therefore if you mutate any element of the array, you have changed the value of the entire array.

Generic type array do not take empty array as input

import Foundation
func insertionSort<T where T: Comparable>(var items:[T])-> [T] {
for (index, _) in items.enumerate().dropFirst() {
var j = index
while ((j > 0) && (items[j] < items[j-1])) {
swap(&items[j], &items[j-1])
j = j-1
}
}
return items
}
// Test the function
insertionSort([]) // Generic type array is not taking empty array
When I am trying to call insertionSort with empty array, I get
Cannot invoke 'insertionSort' with an argument list of type '([_])'
I am not able to figure out how to fix this.
To call generic functions in Swift, Swift needs to be able to infer the generic parameters.
One way giving the type information to Swift, is using an intermediate variable.
(Like noted in Lu_'s comment.)
let arr: [Int] = []
let result = insertionSort(arr)
Another way is using as.
let result = insertionSort([] as [Int])
(Remember, var parameter does not modify the actual argument. It just makes a mutable copy, but does not write it back to the original argument. Swift 3 removed var parameters, as it's so confusing. You may need to assign the return value of the function to a variable.)

Storing a reference to array in swift

I want to pass an array to an object and store a reference to this array. I want to be able to modify this array within this object and make sure that it's modified everywhere else.
Here is what I am trying to accomplish (how the code doesn't work)
class Foo {
var foo : Array<Int>
init(foo: Array<Int>) {
self.foo = foo
}
func modify() {
foo.append(5)
}
}
var a = [1,2,3,4]
let bar = Foo(a)
bar.modify()
print(a) // My goal is that it will print 1,2,3,4,5
My findings so far
A) The array (by default) are passed strange way. It's a reference until you modify an array length. As soon as you modify a length it will be copied and modified. As result, if I append or delete anything from it in the object it won't be seen outside
B) I can use inout on a function parameter. This will allow me to modify it within this function. However, as soon as I will try to assign it to some object member I am again struck by A)
C) I can wrap an array in some Container class. This probably is the cleanest way. However, I serialize/deserialize these objects and I would rather not put it in Container (because I will have to work around some things for serialization and deserialization and sending it to the server).
Are there anything else? Am I missing some Swift construct which allows me to do that?
You'll have to use an NSArray or NSMutableArray for this because Swift Arrays are value types so any assignment will make a copy.
You could make use of Swifts (very un-swifty) UnsafeMutablePointer.
Since (from your post) the behaviour references to arrays can't really seem be trusted, instead keep an UnsafeMutablePointer companion to the class inner array foo as well as any "external" arrays that you want to be binded to foo, in the sense that they are both just pointers to same address in memory.
class Foo {
var foo : [Int]
var pInner: UnsafeMutablePointer<Int>
init(foo: [Int]) {
pInner = UnsafeMutablePointer(foo)
self.foo = Array(UnsafeBufferPointer(start: pInner, count: foo.count))
}
func modify(inout pOuter: UnsafeMutablePointer<Int>) {
foo.append(5) // <-- foo gets new memory adress
pInner = UnsafeMutablePointer(foo)
pOuter = pInner
}
}
var a = [1,2,3,4] // first alloc in memory
var pOuter: UnsafeMutablePointer<Int> = UnsafeMutablePointer(a)
var bar = Foo(foo: a) // 'bar.foo' now at same address as 'a'
print(bar.foo) // [1,2,3,4]
bar.modify(&pOuter) // -> [1,2,3,4,5]
a = Array(UnsafeBufferPointer(start: pOuter, count: bar.foo.count))
/* Same pointer adress, OK! */
print(bar.pInner)
print(pOuter)
/* Naturally same value (same address in memory) */
print(bar.foo)
print(a)
Pointers can be dangerous though (hence the fitting type name), and, again, very un-swifty. Anyway...
/* When you're done: clear pointers. Usually when using
pointers like these you should take care to .destroy
and .dealloc, but here your pointers are just companions
to an Array property (which has a pointer an reference
counter itself), and the latter will take care of the
objects in memory when it goes out of scope. */
bar.pInner = nil
pOuter = nil
Now, what happens when either a or foo goes out of scope, will it break the variable that are not out of scope, or does Swift contain some clever reference counting that realises a memory address is still in use? I haven't investigated this, but feel free to indulge yourself in that.
From the Swift Programming Language,
Structures are always copied when they are passed around in your code, and do not use reference counting.
If you examine the contents of the array variable, you will see that indeed the append works:
class Foo {
var foo : Array
init(_ foo: Array) {
self.foo = foo
}
func modify() {
foo.append(5)
}
func printFoo() {
print("self.foo: \(foo)")
}
}
let a = [1,2,3,4]
let bar = Foo(a)
bar.modify()
bar.printFoo()
print("a: \(a)")
produces
self.foo: [1, 2, 3, 4, 5]
a: [1, 2, 3, 4]
You have taken a copy of a, not a reference to a.
a is declared a constant hence cannot be modified. If you are planning to modify the contents of a, declare it as a variable. i.e.,
var a = [1,2,3,4]
I haven't tested this but, as you are using a class to wrap the array, I see no reason why the following would not work.
class Foo {
var foo : Array<Int>
init(foo: inout Array<Int>) {
self.foo = foo
}
func modify() {
foo.append(5)
}
}
let a = [1,2,3,4]
let bar = Foo(&a)
bar.modify()
print("a: \(a)") // a: [1,2,3,4,5]

Getting the indexes of NSButtons in an array and appending them to an array of integers in swift (Cocoa)

I'm super new to programming so this may be a pretty basic question.
I have an array of NSButtons (checkboxes) in my ViewController that I am calling "buttonArray." I want to go through the array, find out which buttons are checked and add the indexes of those checked buttons to another array of integers (located in a struct), which I am calling "intArray." This is the struct:
struct GetInterval {
var intArray = [Int]()
mutating func putIntIntoArray (intToAdd:Int) {
self.intArray.append(intToAdd)
}
}
I have tried doing this two ways (shown below), both of which have given me compiler errors.
First, I tried to use a function to import the index as an Int and add it to the "intArray" array...
for interval in buttonArray {
if interval.state == NSOnState {
var intervalIndex = find(buttonArray, interval)
GetInterval.putIntIntoArray(Int(intervalIndex!))
}
}
...which gave me the error: "Cannot invoke 'putIntoArray' with argument list of type ((int))"
When that didn't work, I tried appending it directly from the "if" statement in the ViewController...
for interval in buttonArray {
if interval.state == NSOnState {
var intervalIndex = find(buttonArray, interval)
GetInterval.intArray.append(intervalIndex!)
}
}
...which gives me the error: "GetInterval.Type does not have a member named 'intArray'"
How can I fix this?
When you say GetInterval.(something), this refers to a static member (a.k.a., something owned by the GetInterval type). However, your intArray is a member owned by each instance of the GetInterval type. So you need to create an instance first. For example:
var getInt = GetInterval() // create a new instance
getInt.putIntIntoArray(...) // modify this instance, by calling a mutating function
getInt.intArray.append(...) // modify this instance, by directly accessing a property

Append to an array from another function?

I have this code in which I am appending to an array of a struct in one function. The change does not appear in the other function.
type my struct{
arr []int
}
func New_my() *my {
m := new (my)
return m
}
func (m my) Dosomething(){
m.arr = append(m.arr,1)
m.arr = append(m.arr,2)
m.arr = append(m.arr,3)
}
func (m my) Dosomethingelse(){
fmt.Println(m.arr)
}
func main(){
m:= New_my()
m.Dosomething()
m.Dosomethingelse()
}
The output is:
[]
Please, explain what is happening? Why does the change not appear in the array?
If you are new to go you should totally do the tour of go and the effective go document. Go is a new language and with a strange combination of ideas so the official documentation is the best place to start.
First of all you are using a slice not an array. (Read this to understand slices)
The error in your code is because Dosomething() is defined for my instead of *my. This is explained here.
Just change it to:
func (m *my) Dosomething(){
m.arr = append(m.arr,1)
m.arr = append(m.arr,2)
m.arr = append(m.arr,3)
}
In go everything is passed by value so in your code you are passing a copy of the struct to the function Dosomething(), and because the capacity of the slice is 0, the append function creates a new underlying array and returns a reference to it, and when yo do:
m.arr = append(...)
the new slice (using the new array) is lost because it is stored in m that is a copy of the original struct, if m were a *my the new slice would replace the previous in the arr property.
In Go everything is passed by value, including the this/self/me/m argument provided to receivers (aka methods).
In Go if something does not appear to be passed by value, then either a pointer to it, or a struct containing a pointer (as is the case for string, slice, etc) is being passed.
That means right now your DoSomething gets a copy of the self-object and appends to that.
So in this case Topo is correct, you just need to change DoSomething() to pass its self/this/m argument as a pointer.
func (m *my) Dosomething(){
m.arr = append(m.arr,1)
m.arr = append(m.arr,2)
m.arr = append(m.arr,3)
}
I assume this is toy code, but a couple of notes:
You could write this more efficiently as:
func (m *my) Dosomething(){
m.arr = append(m.arr,[]int{1,2,3}...)
}
And it would be more idiomatic to rename New_my() to newMy()

Resources