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 thatnum >= 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
Why Constant Constraints the Property from a Structure Instance But Not the Class Instance
Cannot Assign Property in Method of Struct
How to Encode a String to Base64 in Swift
Can Swift Convert a Class/Struct Data into Dictionary
How to Create Generic Protocols in Swift
Extend Array Types Using Where Clause in Swift
Unwrapping Multiple Optionals in If Statement
Convert a Two Byte Uint8 Array to a Uint16 in Swift
How to Detect a Tap Gesture Location in Swiftui
Calculate Age from Birth Date Using Nsdatecomponents in Swift
Swift - Iboutletcollection Equivalent
What Does the Dollar Sign Do in Swift/Swiftui
Can Swift Return Value from an Async Void-Returning Block