Swift 3 for loop with increment

后端 未结 5 1761
逝去的感伤
逝去的感伤 2020-11-27 04:23

How do I write the following in Swift3?

for (f = first; f <= last; f += interval)          
{
    n += 1
}

This is my own attempt

<
相关标签:
5条回答
  • 2020-11-27 04:52

    for _ in 0.stride(to: last, by: interval) { n += 1 }

    0 讨论(0)
  • 2020-11-27 04:56

    With Swift 5, you may choose one of the 5 following examples in order to solve your problem.


    #1. Using stride(from:to:by:) function

    let first = 0
    let last = 10
    let interval = 2
    
    let sequence = stride(from: first, to: last, by: interval)
    
    for element in sequence {
        print(element)
    }
    
    /*
    prints:
    0
    2
    4
    6
    8
    */
    

    #2. Using sequence(first:next:) function

    let first = 0
    let last = 10
    let interval = 2
    
    let unfoldSequence = sequence(first: first, next: {
        $0 + interval < last ? $0 + interval : nil
    })
    
    for element in unfoldSequence {
        print(element)
    }
    
    /*
    prints:
    0
    2
    4
    6
    8
    */
    

    #3. Using AnySequence init(_:) initializer

    let anySequence = AnySequence<Int>({ () -> AnyIterator<Int> in
        let first = 0
        let last = 10
        let interval = 2
    
        var value = first
        return AnyIterator<Int> {
            defer { value += interval }
            return value < last ? value : nil
        }
    })
    
    for element in anySequence {
        print(element)
    }
    
    /*
    prints:
    0
    2
    4
    6
    8
    */
    

    #4. Using CountableRange filter(_:) method

    let first = 0
    let last = 10
    let interval = 2
    
    let range = first ..< last
    let lazyCollection = range.lazy.filter({ $0 % interval == 0 })
    
    for element in lazyCollection {
        print(element)
    }
    
    /*
    prints:
    0
    2
    4
    6
    8
    */
    

    #5. Using CountableRange flatMap(_:) method

    let first = 0
    let last = 10
    let interval = 2
    
    let range = first ..< last
    let lazyCollection = range.lazy.compactMap({ $0 % interval == 0 ? $0 : nil })
    
    for element in lazyCollection {
        print(element)
    }
    
    /*
    prints:
    0
    2
    4
    6
    8
    */
    
    0 讨论(0)
  • 2020-11-27 04:59

    Swift 2.2 -> 3.0: Strideable:s stride(...) replaced by global stride(...) functions

    In Swift 2.2, we can (as you've tried in your own attempt) make use of the blueprinted (and default-implemented) functions stride(through:by:) and stride(to:by:) from the protocol Strideable

    /* Swift 2.2: stride example usage */
    let from = 0
    let to = 10
    let through = 10
    let by = 1
    for _ in from.stride(through, by: by) { } // from ... through (steps: 'by')
    for _ in from.stride(to, by: by) { }      // from ..< to      (steps: 'by')
    

    Whereas in Swift 3.0, these two functions has been removed from Strideable in favour of the global functions stride(from:through:by:) and stride(from:to:by:); hence the equivalent Swift 3.0 version of the above follows as

    /* Swift 3.0: stride example usage */
    let from = 0
    let to = 10
    let through = 10
    let by = 1
    for _ in stride(from: from, through: through, by: by) { }
    for _ in stride(from: from, to: to, by: by) { }
    

    In your example you want to use the closed interval stride alternative stride(from:through:by:), since the invariant in your for loop uses comparison to less or equal to (<=). I.e.

    /* example values of your parameters 'first', 'last' and 'interval' */
    let first = 0
    let last = 10
    let interval = 2
    var n = 0
    for f in stride(from: first, through: last, by: interval) { 
        print(f)
        n += 1 
    } // 0 2 4 6 8 10
    print(n) // 6
    

    Where, naturally, we use your for loop only as an example of the passage from for loop to stride, as you can naturally, for your specific example, just compute n without the need of a loop (n=1+(last-first)/interval).

    Swift 3.0: An alternative to stride for more complex iterate increment logic

    With the implementation of evolution proposal SE-0094, Swift 3.0 introduced the global sequence functions:

    • sequence(first:next:),
    • sequence(state:next:),

    which can be an appropriate alternative to stride for cases with a more complex iterate increment relation (which is not the case in this example).

    Declaration(s)

    func sequence<T>(first: T, next: @escaping (T) -> T?) -> 
             UnfoldSequence<T, (T?, Bool)>
    
    func sequence<T, State>(state: State, 
                            next: @escaping (inout State) -> T?) ->
               UnfoldSequence<T, State>
    

    We'll briefly look at the first of these two functions. The next arguments takes a closure that applies some logic to lazily construct next sequence element given the current one (starting with first). The sequence is terminated when next returns nil, or infinite, if a next never returns nil.

    Applied to the simple constant-stride example above, the sequence method is a bit verbose and overkill w.r.t. the fit-for-this-purpose stride solution:

    let first = 0
    let last = 10
    let interval = 2
    var n = 0
    for f in sequence(first: first,
                      next: { $0 + interval <= last ? $0 + interval : nil }) {
        print(f)
        n += 1
    } // 0 2 4 6 8 10
    print(n) // 6
    

    The sequence functions become very useful for cases with non-constant stride, however, e.g. as in the example covered in the following Q&A:

    • Express for loops in swift with dynamic range

    Just take care to terminate the sequence with an eventual nil return (if not: "infinite" element generation), or, when Swift 3.1 arrives, make use of its lazy generation in combination with the prefix(while:) method for sequences, as described in evolution proposal SE-0045. The latter applied to the running example of this answer makes the sequence approach less verbose, clearly including the termination criteria of the element generation.

    /* for Swift 3.1 */
    // ... as above
    for f in sequence(first: first, next: { $0 + interval })
        .prefix(while: { $0 <= last }) {
        print(f)
        n += 1
    } // 0 2 4 6 8 10
    print(n) // 6
    
    0 讨论(0)
  • 2020-11-27 05:05

    We can also use while loop as alternative way

    while first <= last {
        first += interval
    }
    
    0 讨论(0)
  • 2020-11-27 05:12

    Simply, working code for Swift 3.0:

    let (first, last, interval) = (0, 100, 1)
    var n = 0
    for _ in stride(from: first, to: last, by: interval) {
        n += 1
    }
    
    0 讨论(0)
提交回复
热议问题