在Swift 2.2中替换C风格的循环

时间:2021-08-25 22:25:32

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

Swift 2.2弃用了C风格的循环。但是在某些情况下,新的范围运算符不能正常工作。

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.

后者将在运行时失败。我可以使用if包装循环,但它有点混乱。有时候这种循环很有用。

Any thoughts?

有什么想法吗?

Use cases

用例

  1. You need to enumerate all elements of an array, except the last one.
  2. 您需要枚举数组的所有元素,但最后一个元素除外。
  3. 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.
  4. 你需要枚举小数范围内的所有整数,但范围可以是[0.5,0.9],所以没有整数(在一些数学之后),这导致一个空循环。

4 个解决方案

#1


20  

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

虽然它不是“漂亮”,但你可以使用步幅:

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

#2


9  

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.

不完全漂亮,但你可以用范围包装范围:s上限用max(0,..)来确定它永远不会取负值。

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

但是,我更喜欢from.stride(to:by)解决方案(已经在其他答案中提到过,参见迈克尔的答案)。

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

然而,我认为明确地指出from.stride(to:by)整齐地返回一个空的StrideTo(或者,如果转换为数组:一个空数组),如果试图跨越一个小于的数字,这是很有价值的。但通过积极的步伐。例如,从0到-42乘以1不会试图一直跨越“∞ - >-∞ - > -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) 
}

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.

对于这个特定的用例,一个简单的解决方案是使用dropLast()(如Sulthan在您的问题的评论中所描述的),然后是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

对于十进制/双闭区间示例([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

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


4  

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

供参考:在swift 3.0中,stride现在全局定义,这使得循环看起来更自然:

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

#4


0  

For Swift 3 and need to change the "index"

对于Swift 3而且需要更改“索引”

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

#1


20  

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

虽然它不是“漂亮”,但你可以使用步幅:

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

#2


9  

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.

不完全漂亮,但你可以用范围包装范围:s上限用max(0,..)来确定它永远不会取负值。

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

但是,我更喜欢from.stride(to:by)解决方案(已经在其他答案中提到过,参见迈克尔的答案)。

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

然而,我认为明确地指出from.stride(to:by)整齐地返回一个空的StrideTo(或者,如果转换为数组:一个空数组),如果试图跨越一个小于的数字,这是很有价值的。但通过积极的步伐。例如,从0到-42乘以1不会试图一直跨越“∞ - >-∞ - > -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) 
}

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.

对于这个特定的用例,一个简单的解决方案是使用dropLast()(如Sulthan在您的问题的评论中所描述的),然后是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

对于十进制/双闭区间示例([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

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


4  

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

供参考:在swift 3.0中,stride现在全局定义,这使得循环看起来更自然:

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

#4


0  

For Swift 3 and need to change the "index"

对于Swift 3而且需要更改“索引”

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