如何快速反向循环迭代?


186

当我在Playground中使用for循环时,一切正常,直到将for循环的第一个参数更改为最大值。(以降序排列)

这是一个错误吗?还有其他人吗?

for index in 510..509
{
    var a = 10
}

显示将要执行的迭代次数的计数器一直在计时...

在此处输入图片说明



1
放弃c样式循环的明智决定。带回来
乔尔·特普利

请参阅我对Swift 5的回答,其中显示了多达4种相反顺序的迭代方法。
Imanou Petit

Answers:


229

Xcode 6 beta 4添加了两个函数,以一个步骤以外的步骤对范围进行迭代: stride(from: to: by:),它与专用范围一起使用,而stride(from: through: by:),与包含范围一起使用。

要以相反的顺序迭代范围,可以按以下方式使用它们:

for index in stride(from: 5, to: 1, by: -1) {
    print(index)
}
//prints 5, 4, 3, 2

for index in stride(from: 5, through: 1, by: -1) {
    print(index)
}
//prints 5, 4, 3, 2, 1

请注意,这些都不是Range成员函数。它们是返回a StrideToStrideThroughstruct的全局函数,它们的定义与Rangestruct 不同。

此答案的先前版本使用struct 的by()成员函数,该成员函数Range已在beta 4中删除。如果要查看其工作原理,请检查编辑历史记录。


26
使用Swift 2.0,现在是:5.stride(to: 1, by: -1)5.stride(through: 1, by: -1)
Binarian

6
有了Swift 3.0,我们又回到了自由功能的步伐。
Cezar

@Shaun是一个合理而安全的选择。我不能告诉您R程序员经常被其:操作员隐式向下迭代而绊倒,导致他们的循环在他们认为跳过它们时会执行。这对于交互使用非常方便,但是在脚本中非常容易出错,以至于样式指南建议不要使用它
达沃·库伯拉尼克

212

将反向函数应用于范围以向后迭代:

对于Swift 1.2及更早版本:

// Print 10 through 1
for i in reverse(1...10) {
    println(i)
}

它也适用于半开范围:

// Print 9 through 1
for i in reverse(1..<10) {
    println(i)
}

注意: reverse(1...10)创建一个type类型的数组[Int],因此尽管这对于较小的范围可能很好,但是lazy如下所示使用它是明智的选择;stride如果范围较大,则考虑接受可接受的答案。


为避免创建大数组,请使用 lazy与一起使用reverse()。以下测试在Playground中有效运行,表明它没有创建具有1万亿Int秒的数组!

测试:

var count = 0
for i in lazy(1...1_000_000_000_000).reverse() {
    if ++count > 5 {
        break
    }
    println(i)
}

对于 Xcode 7中的Swift 2.0

for i in (1...10).reverse() {
    print(i)
}

请注意,在Swift 2.0中, (1...1_000_000_000_000).reverse()类型为ReverseRandomAccessCollection<(Range<Int>)>,因此可以正常工作:

var count = 0
for i in (1...1_000_000_000_000).reverse() {
    count += 1
    if count > 5 {
        break
    }
    print(i)
}

对于Swift 3.0 reverse(),已重命名为reversed()

for i in (1...10).reversed() {
    print(i) // prints 10 through 1
}

3
reversed可能会复制收藏集。至少,这就是我对上的文档的理解Data
拉斐尔

96

为Swift 3更新

下面的答案是可用选项的摘要。选择最适合您需求的一种。

reversed:范围内的数字

前锋

for index in 0..<5 {
    print(index)
}

// 0
// 1
// 2
// 3
// 4

落后

for index in (0..<5).reversed() {
    print(index)
}

// 4
// 3
// 2
// 1
// 0

reversed:中的元素 SequenceType

let animals = ["horse", "cow", "camel", "sheep", "goat"]

前锋

for animal in animals {
    print(animal)
}

// horse
// cow
// camel
// sheep
// goat

落后

for animal in animals.reversed() {
    print(animal)
}

// goat
// sheep
// camel
// cow
// horse

reversed:具有索引的元素

有时在遍历集合时需要一个索引。为此,您可以使用enumerate(),它返回一个元组。元组的第一个元素是索引,第二个元素是对象。

let animals = ["horse", "cow", "camel", "sheep", "goat"]

前锋

for (index, animal) in animals.enumerated() {
    print("\(index), \(animal)")
}

// 0, horse
// 1, cow
// 2, camel
// 3, sheep
// 4, goat

落后

for (index, animal) in animals.enumerated().reversed()  {
    print("\(index), \(animal)")
}

// 4, goat
// 3, sheep
// 2, camel
// 1, cow
// 0, horse

请注意,正如本·拉赫曼(Ben Lachman)在回答中指出的那样,您可能想做.enumerated().reversed()而不是这样做.reversed().enumerated()(这会使索引号增加)。

步幅:数字

跨步是无需使用范围即可进行迭代的方法。有两种形式。代码末尾的注释显示了范围版本(假设增量大小为1)。

startIndex.stride(to: endIndex, by: incrementSize)      // startIndex..<endIndex
startIndex.stride(through: endIndex, by: incrementSize) // startIndex...endIndex

前锋

for index in stride(from: 0, to: 5, by: 1) {
    print(index)
}

// 0
// 1
// 2
// 3
// 4

落后

将增量大小更改为-1可以后退。

for index in stride(from: 4, through: 0, by: -1) {
    print(index)
}

// 4
// 3
// 2
// 1
// 0

注意to和的through区别。

步幅:SequenceType的元素

以2递增

let animals = ["horse", "cow", "camel", "sheep", "goat"]

2在此示例中,我只是为了显示另一种可能性。

for index in stride(from: 0, to: 5, by: 2) {
    print("\(index), \(animals[index])")
}

// 0, horse
// 2, camel
// 4, goat

落后

for index in stride(from: 4, through: 0, by: -1) {
    print("\(index), \(animals[index])")
}

// 4, goat
// 3, sheep 
// 2, camel
// 1, cow  
// 0, horse 

笔记


52

Swift 4以上

for i in stride(from: 5, to: 0, by: -1) {
    print(i)
}
//prints 5, 4, 3, 2, 1

for i in stride(from: 5, through: 0, by: -1) {
    print(i)
}
//prints 5, 4, 3, 2, 1, 0

28

使用Swift 5,您可以根据需要选择以下四个Playground代码示例之一来解决您的问题。


#1。使用ClosedRange reversed()方法

ClosedRange有一种称为的方法reversed()reversed()方法具有以下声明:

func reversed() -> ReversedCollection<ClosedRange<Bound>>

返回以相反顺序显示集合元素的视图。

用法:

let reversedCollection = (0 ... 5).reversed()

for index in reversedCollection {
    print(index)
}

/*
Prints:
5
4
3
2
1
0
*/

另外,您可以使用Range reversed()方法:

let reversedCollection = (0 ..< 6).reversed()

for index in reversedCollection {
    print(index)
}

/*
Prints:
5
4
3
2
1
0
*/

#2。使用sequence(first:next:)功能

Swift标准库提供了一个名为的函数sequence(first:next:)sequence(first:next:)具有以下声明:

func sequence<T>(first: T, next: @escaping (T) -> T?) -> UnfoldFirstSequence<T>

返回一个由组成的序列,该序列由first重复执行next

用法:

let unfoldSequence = sequence(first: 5, next: {
    $0 > 0 ? $0 - 1 : nil
})

for index in unfoldSequence {
    print(index)
}

/*
Prints:
5
4
3
2
1
0
*/

#3。使用stride(from:through:by:)功能

Swift标准库提供了一个名为的函数stride(from:through:by:)stride(from:through:by:)具有以下声明:

func stride<T>(from start: T, through end: T, by stride: T.Stride) -> StrideThrough<T> where T : Strideable

返回从起始值到最终值(可能包括最终值)的步长,以指定量为步长。

用法:

let sequence = stride(from: 5, through: 0, by: -1)

for index in sequence {
    print(index)
}

/*
Prints:
5
4
3
2
1
0
*/

或者,您可以使用stride(from:to:by:)

let sequence = stride(from: 5, to: -1, by: -1)

for index in sequence {
    print(index)
}

/*
Prints:
5
4
3
2
1
0
*/

#4。使用AnyIterator init(_:)初始化

AnyIterator有一个名为的初始化程序init(_:)init(_:)具有以下声明:

init(_ body: @escaping () -> AnyIterator<Element>.Element?)

创建一个迭代器,将该迭代器包装在其next()方法中。

用法:

var index = 5

guard index >= 0 else { fatalError("index must be positive or equal to zero") }

let iterator = AnyIterator({ () -> Int? in
    defer { index = index - 1 }
    return index >= 0 ? index : nil
})

for index in iterator {
    print(index)
}

/*
Prints:
5
4
3
2
1
0
*/

如果需要,可以通过创建扩展方法Int并将其包装在其中的迭代器来重构先前的代码:

extension Int {

    func iterateDownTo(_ endIndex: Int) -> AnyIterator<Int> {
        var index = self
        guard index >= endIndex else { fatalError("self must be greater than or equal to endIndex") }

        let iterator = AnyIterator { () -> Int? in
            defer { index = index - 1 }
            return index >= endIndex ? index : nil
        }
        return iterator
    }

}

let iterator = 5.iterateDownTo(0)

for index in iterator {
    print(index)
}

/*
Prints:
5
4
3
2
1
0
*/

sequence(first:5,next:{($ 0> 0)?($
0-1

27

对于Swift 2.0及更高版本,您应该对范围集合应用反向

for i in (0 ..< 10).reverse() {
  // process
}

在Swift 3.0中已将其重命名为.reversed()。



5

迅捷4.0

for i in stride(from: 5, to: 0, by: -1) {
    print(i) // 5,4,3,2,1
}

如果要包括to值:

for i in stride(from: 5, through: 0, by: -1) {
    print(i) // 5,4,3,2,1,0
}

3

如果要遍历数组Array或更普遍地说,SequenceType反向)。您还有其他一些选择。

首先,您可以reverse()像平常一样在数组中循环遍历。但是我更喜欢使用enumerate(),由于它输出一个包含对象及其索引的元组,因此很多时间。

这里要注意的一件事是,以正确的顺序调用它们很重要:

for (index, element) in array.enumerate().reverse()

按降序产生索引(这是我通常所期望的)。而:

for (index, element) in array.reverse().enumerate() (这与NSArray的匹配度更高 reverseEnumerator

向后移动数组,但输出升序索引。


3

至于Swift 2.2,Xcode 7.3(2016年6月10日):

for (index,number) in (0...10).enumerate() {
    print("index \(index) , number \(number)")
}

for (index,number) in (0...10).reverse().enumerate() {
    print("index \(index) , number \(number)")
}

输出:

index 0 , number 0
index 1 , number 1
index 2 , number 2
index 3 , number 3
index 4 , number 4
index 5 , number 5
index 6 , number 6
index 7 , number 7
index 8 , number 8
index 9 , number 9
index 10 , number 10


index 0 , number 10
index 1 , number 9
index 2 , number 8
index 3 , number 7
index 4 , number 6
index 5 , number 5
index 6 , number 4
index 7 , number 3
index 8 , number 2
index 9 , number 1
index 10 , number 0

2

您可以考虑使用C-Style while循环。在Swift 3中效果很好

var i = 5 
while i > 0 { 
    print(i)
    i -= 1
}

2

您可以使用reversed()方法轻松反转值。

var i:Int
for i in 1..10.reversed() {
    print(i)
}

reversed()方法将值取反。


为什么要添加与现有答案相同的答案?(例如,@ Rohit's。)
Davor Cubranic,

1
var sum1 = 0
for i in 0...100{
    sum1 += i
}
print (sum1)

for i in (10...100).reverse(){
    sum1 /= i
}
print(sum1)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.