Swift 2.2中C风格循环的替代方案

8

Swift 2.2废弃了C风格的循环。然而在某些情况下,新的范围操作符并不能完全替代它。

for var i = 0; i < -1; ++i { ... }

并且

for i in 0..<-1 { ... }

后者在运行时会失败。我可以用一个if来包裹循环,但这样有点杂乱。有时这种类型的循环是有用的。

有什么想法吗?

使用场景

  1. 你需要枚举数组的所有元素,除了最后一个。
  2. 你需要枚举十进制范围内的所有整数,但范围可能像[0.5, 0.9]一样,所以没有整数(经过一些数学计算)导致一个空循环。

1
即使在Swift 2.2中是有效的,内部代码块也不会执行。 - Code Different
你只能使用for循环来迭代或清理输入,例如for i in 0 ..< max(0, -1)。或者使用while循环。 - Sulthan
嘿,使用 max 实际上是一个相当不错的想法!谢谢。但仍然不如 C 风格的简洁 :( - Khanh Nguyen
@Sulthan 非常感谢!如果涉及到数组,那就可以这样做。对于小数情况有什么想法吗?如果有一个范围运算符,当下限大于上限时返回空集合,那将非常方便。 - Khanh Nguyen
1
@KhanhNguyen 我可以举出许多我自己代码中的例子,失去了C风格的for循环有点灾难。虽然这些问题都是可以解决的,但结果远不如优雅。我有时候想知道那些囚犯是如何掌管Swift疯人院的... - matt
显示剩余8条评论
4个回答

20

尽管它不太“好看”,但你可以使用stride

for var i in 0.stride(to: -1, by: -1) {
    print(i)
}

1
我同意,某人进行了驱动式的恶评,可能并没有理解这个解决方案。我认为 by 应该是 1 而不是 -1,以反映 OP 提问的问题: 0.stride(to: -1, by: 1) ... 将产生一个空的跨步范围,相当于 OP 描述的 "C 风格循环",而 0.stride(to: -1, by: -1) ... 将同样产生一个空的跨步范围 (因为严格的 to<..from)。而例如 0.stride(to: -2, by: -1) ... 则不会产生空的跨步范围。 - dfrib

9

模仿“C式循环”

这并不完美,但您可以使用max(0, ...)将范围的上限包装起来,以确保它永远不会取负值。

let foo : [Int] = []
for i in 0..<max(0,foo.count-1) {
    print(i)
}

然而,我更喜欢已经在其他答案中提到的from.stride(to:by)解决方案。

然而,我认为有价值的是明确指出,from.stride(to:by)如果尝试通过正步幅向小于from的数字进行跨越,则会整洁地返回一个空的StrideTo(或者,如果转换为数组:一个空数组)。例如,从0-421的步幅跨越不会尝试穿过"∞ -> -∞ -> -42"(即错误情况),而只是返回一个空的StrideTo(应该如此):

Array(0.stride(to: -42, by: 1)) // []

// -> equivalent to your C loop:
for i in 0.stride(to: foo.count-1, by: 1) { 
    print(i) 
}

应用场景1:列举一个数组中除最后一个元素外的所有元素

对于这个特定的用例,简单的解决方案是使用dropLast()(如Sulthan在您的问题的评论中所述),然后使用forEach

let foo = Array(1...5)
foo.dropLast().forEach { print($0) } // 1 2 3 4

或者,如果您需要更多关于要删除哪些内容的控制,请向数组应用过滤器。
let foo = Array(1...5)
foo.filter { $0 < foo.count }.forEach { print($0) } // 1 2 3 4

用例2:枚举一个十进制范围内的所有整数,允许此枚举为空

对于您的十进制/双精度闭区间示例([0.6, 0.9];在Swift语法上下文中是一个区间而不是一个范围),您可以使用ceil函数将闭区间转换为整数范围,并对后者应用forEach

let foo : (ClosedInterval<Double>) -> () = {
    (Int(ceil($0.start))..<Int(ceil($0.end)))
        .forEach { print($0) }
}

foo(0.5...1.9) // 1
foo(0.5...0.9) // nothing

或者,如果您想特别枚举该区间内包含的(可能的)整数,请使用适合您目的的扩展:

protocol MyDoubleBounds {
    func ceilToInt() -> Int
}

extension Double: MyDoubleBounds {
    func ceilToInt() -> Int {
        return Int(ceil(self)) // no integer bounds check in this simple example
    }
}

extension ClosedInterval where Bound: MyDoubleBounds {
    func enumerateIntegers() -> EnumerateSequence<(Range<Int>)> {
        return (self.start.ceilToInt()
            ..< self.end.ceilToInt())
            .enumerate()
    }
}

示例用法:

for (i, intVal) in (1.3...3.2).enumerateIntegers() {
    print(i, intVal)
} /* 0 2
     1 3 */

for (i, intVal) in (0.6...0.9).enumerateIntegers() {
    print(i, intVal)
} /* nothing */

4

参考信息: 在 Swift 3.0 中,stride 现在被全局定义,这使得 for 循环看起来更加自然:

for i in stride(from: 10, to: 0, by: -1){
    print(i)
} /* 10 9 8 7 6 5 4 3 2 1 */

0

对于Swift 3,需要更改"index"

for var index in stride(from: 0, to: 10, by: 1){}

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