Replacement for C-style loop in Swift 2.2

痴心易碎 提交于 2019-12-17 04:34:25

问题


Swift 2.2 deprecated the C-style loop. However in some cases, the new range operator just doesn't work the same.

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

and

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

The later one will fail at run-time. I can wrap the loop with an if, but it's a bit cluttered. Sometimes this kind of loop is useful.

Any thoughts?

Use cases

  1. You need to enumerate all elements of an array, except the last one.
  2. You need to enumerate all whole integer numbers in a decimal range, but the range can be like [0.5, 0.9] and so there's no integers (after some maths), which results in an empty loop.

回答1:


Although it's not as "pretty", you can use stride:

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



回答2:


Mimicking the "C-style loop"

Not entirely pretty, but you can wrap the range:s upper bound with a max(0, ..) to ascertain it never takes negative values.

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

I'd prefer, however, the from.stride(to:by) solution (that has already been mentioned in the other answers, see e.g. Michael:s answer).

I think it's valuable to explicitly point out, however, that from.stride(to:by) neatly returns an empty StrideTo (or, if converted to an array: an empty array) if attempting to stride to a number that is less than from but by a positive stride. E.g., striding from 0 to -42 by 1 will not attempt to stride all the way through "∞ -> -∞ -> -42" (i.e., an error case), but simply returns an empty StrideTo (as it should):

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) 
}

Use case 1: enumerate all but the last element of an array

For this specific use case, a simple solution is using dropLast() (as described by Sulthan in the comments to your question) followed by forEach.

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

Or, if you need more control over what to drop out, apply a filter to your array

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

Use case 2: enumerate all integers in a decimal range, allowing this enumeration to be empty

For your decimal/double closed interval example ([0.6, 0.9]; an interval rather than a range in the context of Swift syntax), you can convert the closed interval to an integer range (using ceil function) and apply a forEach over the latter

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

Or, if you specifically want to enumerate the (possible) integers contained in this interval; use as en extension fit to your purpose:

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()
    }
}

Example usage:

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 */



回答3:


For reference: In swift 3.0 stride is now defined globally which makes for loop look more natural:

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



回答4:


For Swift 3 and need to change the "index"

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


来源:https://stackoverflow.com/questions/36166907/replacement-for-c-style-loop-in-swift-2-2

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!