如何以相反的顺序快速迭代for循环?

Posted

技术标签:

【中文标题】如何以相反的顺序快速迭代for循环?【英文标题】:How to iterate for loop in reverse order in swift? 【发布时间】:2014-07-01 10:59:32 【问题描述】:

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

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

for index in 510..509

    var a = 10

显示将要执行的迭代次数的计数器一直在滴答作响……

【问题讨论】:

Reverse Range in Swift 的可能重复项 放弃 c 样式循环真是一个绝妙的决定。把他们带回来 参见my answer with Swift 5,它显示了多达 4 种不同的反向迭代方式。 只需.reversed(),如for index in (509 ... 510).reversed() ... 【参考方案1】:

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 成员函数。它们是返回 StrideToStrideThrough 结构的全局函数,它们的定义与 Range 结构不同。

此答案的先前版本使用 Range 结构的 by() 成员函数,该函数已在 beta 4 中删除。如果您想了解它是如何工作的,请查看编辑历史记录。

【讨论】:

使用 Swift 2.0 现在是:5.stride(to: 1, by: -1)5.stride(through: 1, by: -1) 在 Swift 3.0 中,我们又回到了大步作为一个免费功能。 @Shaun 这是一个合理且安全的选择。我不能告诉你 R 程序员经常被它的 : 运算符隐式向下迭代而绊倒,导致他们的循环在他们认为会跳过它们时执行。交互式使用非常方便,但在脚本中非常容易出错,以至于样式指南recommend against using it。【参考方案2】:

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

对于 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) 创建了一个 [Int] 类型的数组,因此虽然这对于小范围可能很好,但明智的做法是使用 lazy,如下所示,或者如果您的范围考虑接受 stride 答案很大。


为避免创建大型数组,请使用 lazyreverse()。以下测试在 Playground 中高效运行,表明它没有创建一个包含一万亿 Ints 的数组!

测试:

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&lt;(Range&lt;Int&gt;)&gt;,因此可以正常工作:

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


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

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

【讨论】:

reversed 可能会复制该集合。至少,我是这样理解Data 上的文档的。【参考方案3】:

为 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

reversedSequenceType 中的元素

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 在 his answer 中指出的那样,您可能想要使用 .enumerated().reversed() 而不是 .reversed().enumerated()(这会使索引号增加)。

步幅:数字

Stride 是在不使用范围的情况下进行迭代的方式。有两种形式。代码末尾的 cmets 显示范围版本将是什么(假设增量大小为 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

注意tothrough 的区别。

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

注意事项

@matt 有an interesting solution,他在其中定义了自己的反向运算符并将其称为&gt;&gt;&gt;。不需要太多代码来定义,使用方式如下:

for index in 5>>>0 
    print(index)


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

查看On C-Style For Loops Removed from Swift 3

【讨论】:

【参考方案4】:

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

【讨论】:

【参考方案5】:

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


#1。使用ClosedRangereversed()方法

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>

返回由firstnext 的重复惰性应用形成的序列。

用法:

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。使用AnyIteratorinit(_:)初始化器

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) : nil ) // 比 ($0 - 1 >= 0) 简单【参考方案6】:

对于 Swift 2.0 及更高版本,您应该在范围集合上应用 reverse

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

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

【讨论】:

【参考方案7】:

在 Swift 4 及更高版本中

    let count = 50//For example
    for i in (1...count).reversed() 
        print(i)
    

【讨论】:

【参考方案8】:

Swift 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

【讨论】:

【参考方案9】:

如果想要反向遍历一个数组Array 或更一般的SequenceType)。您还有一些其他选择。

首先,您可以reverse() 数组并正常循环遍历它。不过我更喜欢在很多时候使用enumerate(),因为它会输出一个包含对象及其索引的元组。

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

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

以降序生成索引(这是我通常期望的)。而:

for (index, element) in array.reverse().enumerate()(更接近 NSArray 的 reverseEnumerator

向后遍历数组,但输出升序索引。

【讨论】:

【参考方案10】:

至于 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

【讨论】:

【参考方案11】:

您可以考虑改用 C 样式 while 循环。这在 Swift 3 中运行良好:

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

【讨论】:

【参考方案12】:

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

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

reversed() 方法反转值。

【讨论】:

为什么要添加一个与现有答案相同的答案? (例如,@Rohit's。)【参考方案13】:
var sum1 = 0
for i in 0...100
    sum1 += i

print (sum1)

for i in (10...100).reverse()
    sum1 /= i

print(sum1)

【讨论】:

【参考方案14】:

只需一步即可反转数组.reverse()

var arrOfnum = [1,2,3,4,5,6]
arrOfnum.reverse()

【讨论】:

【参考方案15】:

这将以相反的顺序减一。

let num1 = [1,2,3,4,5]
for item in nums1.enumerated().reversed()  

    print(item.offset) // Print the index number: 4,3,2,1,0
    print(item.element) // Print the value :5,4,3,2,1


或者你可以使用这个索引、值属性

let num1 = [1,2,3,4,5]
for (index,item) in nums1.enumerated().reversed()  

    print(index) // Print the index number: 4,3,2,1,0
    print(item) // Print the value :5,4,3,2,1


【讨论】:

【参考方案16】:

对我来说,这是最好的方式。

var arrayOfNums = [1,4,5,68,9,10]

for i in 0..<arrayOfNums.count 
    print(arrayOfNums[arrayOfNums.count - i - 1])

【讨论】:

以上是关于如何以相反的顺序快速迭代for循环?的主要内容,如果未能解决你的问题,请参考以下文章

csharp 示例演示如何使用for循环以相反顺序迭代数组。从计算机编程基础知识到C#http:/

可以以相反的顺序为java中的每个循环做一个吗?

如何在表格视图单元格中快速迭代for循环

Berkeley Db db_map 迭代器没有以正确的顺序循环

for循环和迭代器Iterator

不能在 swift 中使用 for...in 或 forEach 循环来快速打印视图