Express for loops in swift with dynamic range
for-loop, iteration, swift, swift3
Solution
Using a helper function (originally defined at Converting a C-style for loop that uses division for the step to Swift 3)
public func sequence<T>(first: T, while condition: @escaping (T)-> Bool, next: @escaping (T) -> T) -> UnfoldSequence<T, T> {
let nextState = { (state: inout T) -> T? in
// Return `nil` if condition is no longer satisfied:
guard condition(state) else { return nil }
// Update current value _after_ returning from this call:
defer { state = next(state) }
// Return current value:
return state
}
return sequence(state: first, next: nextState)
}
you can write the loop as
let num = 1000
for i in sequence(first: 5, while: { num/$0 > 0 }, next: { $0 * 5 }) {
print(i)
}
A simpler solution would be a while-loop:
var i = 5
while num/i > 0 {
print(i)
i *= 5
}
but the advantage of the first solution is that the scope of the loop variable is limited to the loop body, and that the loop variable is a constant.
Swift 3.1 will provide a `prefix(while:)` method for sequences, and then the helper function is no longer necessary:
let num = 1000
for i in sequence(first: 5, next: { $0 * 5 }).prefix(while: { num/$0 > 0 }) {
print(i)
}
All of above solutions are "equivalent" to the given C loop. However, they all can crash if `num` is close to `Int.max` and `$0 * 5` overflows. If that is an issue then you have to check if `$0 * 5` fits in the integer range before doing the multiplication.
Actually that makes the loop simpler – at least if we assume that `num >= 5` so that the loop is executed at least once:
for i in sequence(first: 5, next: { $0 <= num/5 ? $0 * 5 : nil }) {
print(i)
}
Problem
...or how can I use the index inside the for loop condition Hey people Since we're left with no c style for loops in swift 3 I can't seem to find a way to express a bit more complex for loops so maybe you can help me out. If I were to write this ``` for(int i=5; num/i > 0; i*=5) ``` in swift 3 how would I do that? The closes I came by was: ``` for i in stride(from: 5, through: num, by: 5) where num/i > 0 ``` but this will of course iterate 5 chunks at a time instead if i being: 5, 25, 125 etc. Any ideas? Thanks