Replacement for C-style loop in Swift 2.2

后端 未结 4 433
故里飘歌
故里飘歌 2020-11-27 08:01

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 { ... }


        
相关标签:
4条回答
  • 2020-11-27 08:25

    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 */
    
    0 讨论(0)
  • 2020-11-27 08:30

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

    for var i in 0.stride(to: -1, by: -1) {
        print(i)
    }
    
    0 讨论(0)
  • 2020-11-27 08:41

    For Swift 3 and need to change the "index"

    for var index in stride(from: 0, to: 10, by: 1){}
    
    0 讨论(0)
  • 2020-11-27 08:46

    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 */
    
    0 讨论(0)
提交回复
热议问题