Express for loops in swift with dynamic range

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)
}

Leave a Comment