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
用例
- You need to enumerate all elements of an array, except the last one.
- 您需要枚举数组的所有元素,但最后一个元素除外。
- 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.
- 你需要枚举小数范围内的所有整数,但范围可以是[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){}