在Swift中,for循环的条件会在每次循环时进行评估吗?

4
我是一名有用的助手,可以为您翻译文本。
我在工作中有一个小辩论:在遍历数组项之前,计算Swift数组的大小是否是一个好的实践?什么是更好的代码实践:
选项A:
    func setAllToFalse() {
        for (var i = 0; i < mKeyboardTypesArray.count; i++ ) {
            self.mKeyboardTypesArray[i] = false
        }
    }

选项B:

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

当然,这是在循环期间我不修改数组时的情况。

我查阅了文档,其中说明如下:

循环的执行顺序如下:

当第一次进入循环时,初始化表达式会被评估一次,以设置循环所需的任何常量或变量。条件表达式会被评估。如果它计算结果为 false,则循环结束,代码执行将继续在 for 循环的右括号(})之后。如果表达式计算结果为 true,则代码执行继续通过执行括号内的语句。在执行完所有的语句后,增量表达式将被评估。它可能会增加或减少计数器的值,或根据语句的结果将一个已初始化的变量设置为新值。在评估增量表达式后,执行返回到步骤2,并再次评估条件表达式。


@Kametrixom,你说得对。然而,在上面显示的示例中你不能使用for..in语法,因为你得到的val实际上是一个let变量,它不能被更改。所以你必须使用更长的语法。 - Emil Adz
非常抱歉,我真的以为那是可能的。 - Kametrixom
1
但是我完全忘了0..<array.count语法,这对于那些情况非常完美。 - Emil Adz
1
是的,我真的很喜欢 Swift 有许多不同的 for 循环语法。 - Kametrixom
这就是为什么我使用注释的原因。 - Kametrixom
显示剩余7条评论
3个回答

1
这句话在 Swift 中的惯用方式是:


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

那样做就没有什么需要评估和计算的了。实际上,这将成为一个不错的数组方法:
extension Array {
    mutating func setAllTo(newValue:T) {
        self = self.map {_ in newValue}
    }
}

现在你可以直接说:
mKeyboardTypesArray.setAllTo(false)

或者,您可以以这种方式完成(这涉及仅一次获取 count ):

mKeyboardTypesArray = Array(count:mKeyboardTypesArray.count, repeatedValue:false)

并没有真正回答这个问题,但我通常更倾向于使用函数式编程,它可以使代码看起来更加优美,并消除许多潜在的 bug。 - Kametrixom
我不想引起争论,但我认为他的意思是选项A选项B - Kametrixom
2
那些是C风格的循环。那不是良好的Swift风格。那是我的答案。如果你有不同的答案,请给出你的答案。让群众决定。请不要一直用评论来烦我。谢谢。 - matt

0

我发现这不是一个好的方法,如果你正在遍历一个数组并删除其中的元素(例如),它可能会导致程序崩溃。在当前的Swift语法中,这个for循环:

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

在数组的一半崩溃了,索引错误。

我把它改成了while循环:

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

0
循环条件在每次循环时都会被评估。考虑这个实验:
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])
}

输出:

这里
1
这里
2
这里
3
这里
4
这里
5
这里

您可以从选项B中看到一些小的速度提升,但我认为如果数组未更改,则Array上的count属性不会很昂贵。这可能是一个良好的代码实践,因为它向读者传达了您期望数组大小在循环期间保持不变。

编译器可能通过检测循环中没有修改数组来优化array.count,但由于println的副作用,它无法对array.myCount进行优化。


我认为整个问题的关键在于优化器是否可以优化掉“array.count”调用,而不是关于调用具有特殊副作用的方法。 - StilesCrisis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接