4

I have a small debate at work: Is it a good practice to calculate the size of the Array in swift before running over it's items? What would be a better code practice:

Option A:

    func setAllToFalse() {
        for (var i = 0; i < mKeyboardTypesArray.count; i++ ) {
            self.mKeyboardTypesArray[i] = false
        }
    }

or Option B:

    func setAllToFalse() {
        let typesCount = mKeyboardTypesArray.count
        for (var i = 0; i < typesCount; i++ ) {
            self.mKeyboardTypesArray[i] = false
        }
    }

All, of course, if I don't alter the Array during the loops.

I did go over the documentation, which states this:

The loop is executed as follows:

When the loop is first entered, the initialization expression is evaluated once, to set up any constants or variables that are needed for the loop. The condition expression is evaluated. If it evaluates to false, the loop ends, and code execution continues after the for loop’s closing brace (}). If the expression evaluates to true, code execution continues by executing the statements inside the braces. After all statements are executed, the increment expression is evaluated. It might increase or decrease the value of a counter, or set one of the initialized variables to a new value based on the outcome of the statements. After the increment expression has been evaluated, execution returns to step 2, and the condition expression is evaluated again.

12
  • I think you'd have to either study the generated assembly code or wait until Swift is open-source. The Swift compiler is supposed to be very very very smart, there's lots of optimizations. Also if you're sure that the array doesn't change, you can use: for val in array { ... } which will be the fastest way for sure. Commented Jul 5, 2015 at 22:44
  • @Kametrixom, you are right. Yet you can't use the for.. in syntax in the example shown above as the val you get is actually a let variable, and it can't be changed, So you have to go to the longer syntax. Commented Jul 5, 2015 at 22:48
  • You can use for var val in array { ... } ;). But i know what you mean, you can use for i in 0..<array.count { ... } which definitely won't call .count ever time Commented Jul 5, 2015 at 22:50
  • 1
    But I totally forgot about the 0..<array.count syntax which is perfect for those cases. Commented Jul 5, 2015 at 22:56
  • 1
    Yeah I really like that Swift has lots of different for loop syntax(es?) Commented Jul 5, 2015 at 22:58

3 Answers 3

1

The idiomatic way to say this in Swift is:

func setAllToFalse() {
    mKeyboardTypesArray = mKeyboardTypesArray.map {_ in false}
}

That way, there is nothing to evaluate and nothing to count.

In fact, this would make a nice Array method:

extension Array {
    mutating func setAllTo(newValue:T) {
        self = self.map {_ in newValue}
    }
}

Now you can just say:

mKeyboardTypesArray.setAllTo(false)

Alternatively, you could do it this way (this involves taking the count, but only once):

mKeyboardTypesArray = Array(count:mKeyboardTypesArray.count, repeatedValue:false)
Sign up to request clarification or add additional context in comments.

3 Comments

Doesn't really answer the question, but I generaly tend to use functional programming more, it can make de look much nicer and removes a lot of potential bugs
I don't wanna start an argument, but I think he meant Option A or Option B
Those are C-style for loops. That is not good Swift style. That is my answer. If you have a different answer, give your answer. Let the crowd decide. Don't keep nagging me with comments, please. Thanks.
0

The loop condition is evaluated each time through the loop. Consider this experiment:

extension Array {
    var myCount: Int {
        get {
            println("here")
            return self.count
        }
    }
}

let a = [1, 2, 3, 4, 5]

for var i = 0; i < a.myCount; i++ {
    println(a[i])
}

Output:

here
1
here
2
here
3
here
4
here
5
here

You could see a small speed improvement from Option B, but I expect that the count property on Array is not expensive if the array is unchanged. It is potentially a good code practice anyway because it communicates to the reader that you expect the array size to remain constant for duration of the loop.

It is possible that the compiler would optimize array.count by detecting that nothing in the loop modifies the array, but it wouldn't be able to do that for array.myCount because of the println side effect.

1 Comment

I think the entire question revolves around whether the optimizer can optimize away the "array.count" call. Not about calling a method with special side effects.
0

I have found that it is not, which can cause a crash if you're iterating through an array and removing items (for example). In current Swift syntax, this for loop

        for i in 0..<m_pendingCommands.count
        {
            if m_pendingCommands[i].packetID < command.packetID
            {
                m_pendingCommands.remove(at: i)
            }
        }

crashed halfway through the array, with a bad index.

I switched this to a while loop:

    var i: Int = 0
    while i < m_pendingCommands.count
    {
        if m_pendingCommands[i].packetID < ID
        {
            m_pendingCommands.remove(at: i)
        }
        else
        {
            i += 1
        }
    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.