Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do Swift array insert and removeAtIndex operations behave inconsistently?

Tags:

arrays

ios

swift

I believe it is legal to swap the 0th and 1st item of a Swift array as follows:

  • call removeAtIndex at index 0, which shuffles the 1st item back to index 0
  • insert the removed item at index 1.

But I am seeing inconsistent behaviour depending on how I code it.

Code

func test() {

    class Test {

        var array = ["foo", "bar"]

        func swap1() {       // PRODUCES STRANGE RESULT
            array.insert(array.removeAtIndex(0), atIndex:1)
            print("---swap1---", xs:array)
        }

        func swap2() {       // PRODUCES EXPECTED RESULT
            let item = array.removeAtIndex(0)
            array.insert(item, atIndex:1)
            print("---swap2---", xs:array)
        }

        func swap3() {       // PRODUCES EXPECTED RESULT
            var array = ["foo", "bar"]
            array.insert(array.removeAtIndex(0), atIndex:1)
            print("---swap3---", xs:array)
        }

        func print(fn: String, xs: [String]) {
            println(fn)
            for x in xs { println(x) }
        }
    }

    Test().swap1()
    Test().swap2()
    Test().swap3()
}

Output

---swap1---
foo
foo
bar
---swap2---
bar
foo
---swap3---
bar
foo

Unexpectedly (to me), swap1() clones the 0th element rather than removes it, as can be seen from the repeated "foo" in the swap1 output above. The characteristics that seem to produce this behaviour are

  • passing the result of removeAtIndex() as the first parameter to the insert() call, rather than storing it in an intermediate constant (latter done by swap2())
  • operating on a class member variable rather a function local variable (latter done by swap3()).

My question: why does swap1() behave differently?

like image 581
PDFM Avatar asked Nov 01 '22 19:11

PDFM


1 Answers

You have two mutators of array in the same statement. This generally is a bad idea and results in undefined behavior. It's the same reason you don't want to do:

a = a++ + a++;

From the human point of view, it is clear that you are first removing an item, and then adding that back to the array it has been removed from.

From the compiler's point of view, it must do 3 things to modify the array: 1) read the contents of the original array, 2) modify the contents, and 3) write them back. The compiler has to do this twice in the statement:

array.insert(array.removeAtIndex(0), atIndex:1)

You expected the compiler to do this:

1) temp1 = array
2) temp1.removeItem
3) array = temp1
4) temp2 = array
5) temp2.addItem
6) array = temp2

but instead it did:

1) temp1 = array
2) temp2 = array
3) temp2.removeItem
4) array = temp2
5) temp1.addItem
6) array = temp1

The compiler's order of operations is allowable which is why you shouldn't put two mutators in the same statement.

like image 131
vacawama Avatar answered Nov 08 '22 03:11

vacawama