计数器作为for-in-loops中的变量

Counter as variable in for-in-loops

当通常使用 for-in-loop 时,计数器(在本例中 number)在每次迭代中都是一个常量:

for number in 1...10 {
    // do something
}

这意味着我无法在循环中更改 number:

for number in 1...10 {
    if number == 5 {
        ++number
    }
}

// doesn't compile, since the prefix operator '++' can't be performed on the constant 'number'

有没有办法将 number 声明为变量,而无需在循环之前声明它,或者使用普通的 for 循环(带初始化、条件和增量)?

答案是 "no",这是一件好事。否则,可能会出现像这样非常混乱的行为:

for number in 1...10 {
    if number == 5 {
        // This does not work
        number = 5000
    }
    println(number)
}

想象一下,有人在循环输出中看到数字 5000 时感到困惑,该循环理应绑定到 1 到 10(含)的范围。

此外,Swift 会选择什么作为下一个值 5000?它应该停止吗?它是否应该在分配之前继续到范围内的下一个数字?它应该在超出范围的赋值时抛出异常吗?这三个选择对他们来说都有一定的有效性,所以没有明确的赢家。

为了避免这种情况,Swift 设计者将范围循环中的循环变量设为不可变。

要理解为什么 i 不能可变,需要知道 for…in 是什么 shorthand。 for i in 0..<10 由编译器扩展为以下内容:

var g = (0..<10).generate()
while let i = g.next() {
    // use i
}

每次循环,i 都是一个新声明的变量,解包的值是在生成器上调用 next 的下一个结果。

现在,while可以这样写:

while var i = g.next() {
    // here you _can_ increment i:
    if i == 5 { ++i }
}

但当然,这无济于事——g.next() 下次循环时仍会生成 5。 body 中的增量毫无意义。

大概是因为这个原因,for…in 不支持相同的 var 语法来声明它的循环计数器——如果您不了解它是如何工作的,将会非常混乱。

(与 where 不同,在这里您可以看到正在发生的事情 – var 功能偶尔有用,类似于 func f(var i) 的功能)。

如果你想要跳过循环的某些迭代,你最好的选择(不求助于 C-style forwhile)是使用一个生成器来跳过循环相关值:

// iterate over every other integer
for i in 0.stride(to: 10, by: 2) { print(i) }

// skip a specific number
for i in (0..<10).filter({ [=12=] != 5 }) { print(i) }

let a = ["one","two","three","four"]

// ok so this one’s a bit convoluted...
let everyOther = a.enumerate().filter { [=12=].0 % 2 == 0 }.map { [=12=].1 }.lazy

for s in everyOther {
    print(s)
}

更新Swift 5

for var i in 0...10 {
    print(i)
    i+=1
}