Reverse Range in Swift

前端 未结 7 1253
青春惊慌失措
青春惊慌失措 2020-11-27 14:07

Is there a way to work with reverse ranges in Swift?

For example:

for i in 5...1 {
  // do something
}

is an infinite loop.

相关标签:
7条回答
  • 2020-11-27 14:57

    It appears that the answers to this question have changed a bit as we've progressed through the betas. As of beta 4, both the by() function and the ReversedRange type have been removed from the language. If you're looking to make a reversed range, your options are now as follows:

    1: Create a forward range, and then use the reverse() function to reverse it.

    for x in reverse(0 ... 4) {
        println(x) // 4, 3, 2, 1, 0
    }
    
    for x in reverse(0 ..< 4) {
        println(x) // 3, 2, 1, 0
    }
    

    2: Use the new stride() functions that were added in beta 4, which includes functions to specify the starting and ending indexes, as well as the amount to iterate by.

    for x in stride(from: 0, through: -8, by: -2) {
        println(x) // 0, -2, -4, -6, -8
    }
    
    for x in stride(from: 6, to: -2, by: -4) {
        println(x) // 6, 2
    }
    

    Note that I've also included the new exclusive range operator in this post as well. .. was replaced with ..<.

    Edit: From the Xcode 6 beta 5 release notes, Apple added the following suggestion for handling this:

    ReverseRange has been removed; use lazy(x..

    Here's an example.

    for i in lazy(0...5).reverse() {
        // 0, 1, 2, 3, 4, 5
    }
    
    0 讨论(0)
  • 2020-11-27 15:03

    Reverse() function is used for reverse number.

    Var n:Int // Enter number

    For i in 1...n.reverse() { Print(i) }

    0 讨论(0)
  • 2020-11-27 15:06

    Swift 3, 4+: you can do it like this:

    for i in sequence(first: 10, next: {$0 - 1}) {
    
        guard i >= 0 else {
            break
        }
        print(i)
    }
    

    result: 10, 9, 8 ... 0

    You can customise it any way you like. For more info read func sequence<T> reference

    0 讨论(0)
  • 2020-11-27 15:07

    This could be another way of doing this.

    (1...5).reversed().forEach { print($0) }
    
    0 讨论(0)
  • 2020-11-27 15:09

    There's something troubling about the asymmetry of this:

    for i in (1..<5).reverse()
    

    ...as opposed to this:

    for i in 1..<5 {
    

    It means that every time I want to do a reverse range, I have to remember to put the parentheses, plus I have to write that .reverse() on the end, sticking out like a sore thumb. This is really ugly in comparison to C-style for loops, which are symmetrical counting up and counting down. So I tended to use C-style for loops instead. But in Swift 2.2, C-style for loops are going away! So I've had to scurry around replacing all my decrementing C-style for loops with this ugly .reverse() construct — wondering all the while, why on earth isn't there a reverse-range operator?

    But wait! This is Swift — we're allowed to define our own operators!! Here we go:

    infix operator >>> {
        associativity none
        precedence 135
    }
    
    func >>> <Pos : ForwardIndexType where Pos : Comparable>(end:Pos, start:Pos)
        -> ReverseRandomAccessCollection<(Range<Pos>)> {
            return (start..<end).reverse()
    }
    

    So now I'm allowed to say:

    for i in 5>>>1 {print(i)} // 4, 3, 2, 1
    

    This covers just the most common case that occurs in my code, but it is far and away the most common case, so it's all I need at present.

    I had a kind of internal crisis coming up with the operator. I would have liked to use >.., as being the reverse of ..<, but that's not legal: you can't use a dot after a non-dot, it appears. I considered ..> but decided it was too hard to distinguish from ..<. The nice thing about >>> is that it screams at you: "down to!" (Of course you're free to come up with another operator. But my advice is: for super symmetry, define <<< to do what ..< does, and now you've got <<< and >>> which are symmetrical and easy to type.)


    Swift 3 version (Xcode 8 seed 6):

    infix operator >>> : RangeFormationPrecedence
    func >>><Bound>(maximum: Bound, minimum: Bound) ->
        ReversedRandomAccessCollection<CountableRange<Bound>> 
        where Bound : Comparable, Bound.Stride : Integer {
            return (minimum..<maximum).reversed()
    }
    

    Swift 4 version (Xcode 9 beta 3):

    infix operator >>> : RangeFormationPrecedence
    func >>><Bound>(maximum: Bound, minimum: Bound)
        -> ReversedRandomAccessCollection<CountableRange<Bound>>
        where Bound : Comparable & Strideable { 
            return (minimum..<maximum).reversed()
    }
    

    Swift 4.2 version (Xcode 10 beta 1):

    infix operator >>> : RangeFormationPrecedence
    func >>><Bound>(maximum: Bound, minimum: Bound)
        -> ReversedRandomAccessCollection<Range<Bound>>
        where Bound : Strideable { 
            return (minimum..<maximum).reversed()
    }
    

    Swift 5 version (Xcode 10.2.1):

    infix operator >>> : RangeFormationPrecedence
    func >>><Bound>(maximum: Bound, minimum: Bound)
        -> ReversedCollection<Range<Bound>>
        where Bound : Strideable {
            return (minimum..<maximum).reversed()
    }
    
    0 讨论(0)
  • 2020-11-27 15:12

    Update For latest Swift 3 (still works in Swift 4)

    You can use the reversed() method on a range

    for i in (1...5).reversed() { print(i) } // 5 4 3 2 1
    

    Or stride(from:through:by:) method

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

    stide(from:to:by:) is similar but excludes the last value

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

    Update For latest Swift 2

    First of all, protocol extensions change how reverse is used:

    for i in (1...5).reverse() { print(i) } // 5 4 3 2 1

    Stride has been reworked in Xcode 7 Beta 6. The new usage is:

    for i in 0.stride(to: -8, by: -2) { print(i) } // 0 -2 -4 -6
    for i in 0.stride(through: -8, by: -2) { print(i) } // 0 -2 -4 -6 -8
    

    It also works for Doubles:

    for i in 0.5.stride(to:-0.1, by: -0.1) { print(i) }
    

    Be wary of floating point compares here for the bounds.

    Earlier edit for Swift 1.2: As of Xcode 6 Beta 4, by and ReverseRange don't exist anymore :[

    If you are just looking to reverse a range, the reverse function is all you need:

    for i in reverse(1...5) { println(i) } // prints 5,4,3,2,1
    

    As posted by 0x7fffffff there is a new stride construct which can be used to iterate and increment by arbitrary integers. Apple also stated that floating point support is coming.

    Sourced from his answer:

    for x in stride(from: 0, through: -8, by: -2) {
        println(x) // 0, -2, -4, -6, -8
    }
    
    for x in stride(from: 6, to: -2, by: -4) {
        println(x) // 6, 2
    }
    
    0 讨论(0)
提交回复
热议问题