Express For Loops in Swift With Dynamic Range

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

Swift C-style loop

In Swift 3 you can do

for f in sequence(first: 1, next: { $0 < (1024 / 2) ? $0 * 2 : nil }) {
print(f)
}

The concept of the sequence function is described in the documentation.

Printing an infinite list is easy, the code would just be

for f in sequence(first: 1, next: {$0 * 2}) {
print(f)
}

Since we want the program to stop at some point, we us the ternary operator ? to terminate the list once we reach the maximum value.
Since the last value we want to print is 512, the last value we have to double is 256. For 512 which does not satisfy the condition < (1024 / 2) we have nil and thereby stop.

Is there in Swift a 'for loop' where index can increase by a factor of X

The Swift analog to:

for (int runIndex=1; runIndex <= 100000000; runIndex = runIndex*10){
System.out.println(runIndex);
}

that gives identical output to:

var runIndex = 1
while runIndex <= 100000000 {
print(runIndex)
runIndex = runIndex * 10;
}

is:

for runIndex in (sequence(first: 1) {$0 >= 100000000 ? nil : $0*10}) {
print(runIndex)
}

See https://developer.apple.com/documentation/swift/2015879-sequence and notice that the documentation actually takes your own use case, i.e. successive powers (of 2), as an example!

Converting a C-style for loop that uses division for the step to Swift 3

MartinR's solution is very generic and useful and should be part of your toolbox.

Another approach is to rephrase what you want: the powers of two from 7 down to 0.

for i in (0...7).reversed().map({ 1 << $0 }) {
print(i)
}

Trouble understanding a for loop in Swift 3

// set variable "size" to the value of zero

var size = 0

// set variable "candidate" to be a NAMED TUPLE with initial values (0,0)

var candidate = (value: 0, index: 0)

// start a FOR loop from 0 to "count" which by the way has not been defined yet, so that might be a problem.

for i in 0..<count {

// if size is zero, then we set candidate equal to ARRAY "A" of index "i" and increment 'size'... and by the way, this Array "A" has not been defined anywhere, so that might be a problem.
if size == 0 {
candidate = (A[i], i)
size += 1

// otherwise, we do one of two things: 1) if the 'candidate' tuple's (value:) parameter is NOT equal to the value of Array 'A''s index of 'i', then we decrement 'size' ... by the way this Array 'A' has not yet been defined so that might be a problem... or 2) increment the value of 'size.'

} else {
if candidate.value != A[i] {
size -= 1
} else {
size += 1
}
}
}

So it looks like there should be an array somewhere called 'A'

and 'count' should really be A.count

Basically the loop is (seems to be trying to) going through the Array 'A' and comparing adjacent values and adjusting the 'size' variable as a result of each of these comparisons.

A concise way to not execute a loop now that C-Style for loops are going to be removed from Swift 3?

To do this in a way that works for n < 2, you can use the stride method.

let startIndex = 2
let endIndex = n

for i in stride(from: startIndex, through: endIndex, by: 1) {
memo.append(memo[i-1] + memo[i-2])
}

How to fix C-style for statement has been removed in Swift 3?

I don't think this can be written using for anymore, but you can use while loop to get the job done:

var nSize = merkleTree.count
while nSize > 1 {
// loop body

nSize = (nSize + 1) / 2
}

I would expect stride not to work in this case, because as your error states, you cannot use nSize as the stride parameter - nSize is iterating variable that gets declared based on the range, so you need the range to exist. At least that's my interpretation of the error (I know that theoretically you can generate range based on the previously generated item, but obviously stride does not work that way).

I believe you can find a way to generate a proper array of values using reduce (because I was able to, see below, maybe you can make it simpler), or by implementing your own stride that would accept a closure instead of a step (which would allow you to compute next item based on previous one), but both approaches are more complicated and obscure than using the simple while loop, so I personally prefer the while loop.

My not so nice reduce implementation (in result it uses an array and not a range, since by looking at NSRange I don't think you can create a range that does not step by 1):

let merkleTree = [1,2,3,4,5,6,7,8,9]

let numberOfDivisions = Int(log2(Double(merkleTree.count))) + 1
let startValue = merkleTree.count
let nSizes = (0..<numberOfDivisions).reduce([startValue]) { (result, next) -> [Int] in
var newResult = result
newResult.append((result.last! + 1) / 2)
return newResult
}
print(nSizes)
// and now you can for-in it:
for nSize in nSizes {
// ...
}

What is the nil or empty countable range in Swift?

I am a fan of simple solutions:

let lowerIndex = 1
let higherIndex = 0
for index in lowerIndex ..< max(lowerIndex, higherIndex) {
latter(index)
}


Related Topics



Leave a reply



Submit