A brief way to not run a loop now, when will the C-Style for loops be removed from Swift 3?

Imagine we have this code that works fine for n >= 0 .

 func fibonacci(n: Int) -> Int { var memo = [0,1] for var i = 2; i <= n; i++ { memo.append(memo[i-1] + memo[i-2]) } return memo[n] } 

If I remove the C-style for the loop due to upcoming changes in Swift 3.0 , I get something like this:

 func fibonacci(n: Int) -> Int { var memo = [0,1] for i in 2...n { memo.append(memo[i-1] + memo[i-2]) } return memo[n] } 

While this works fine for n >= 2 , this is crashing for numbers 0 and 1 with this error message:

fatal error: it is impossible to form a range with a final <start

What is the most concise way to fix this code so that it works correctly for 0 and 1 ?

(Note: everything is fine, and even desirable, for negative numbers to collapse the application.)


Note: I understand that I can add security instructions:

guard n >= 2 else { return memo[n] }

... but I hope that there is a better way to fix only the faulty part of the code ( 2...n ).

For example, if there was a compressed way to create a range that returns null elements, if end < start , this would be a more ideal solution.

+7
for-loop swift range
source share
4 answers

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]) } 
+6
source share

You can easily create a valid range using the max() function:

 for i in 2 ..< max(2, n+1) { memo.append(memo[i-1] + memo[i-2]) } 

This computes the empty range 2 ..< 2 if n < 2 .

It is important to use the ..< operator, which excludes the upper bound, because 2 ... 1 not a valid range.

But in this function Iโ€™ll just consider special cases first

 func fibonacci(n: Int) -> Int { // Let it crash if n < 0: precondition(n >= 0, "n must not be negative") // Handle n = 0, 1: if n <= 1 { return n } // Handle n >= 2: var memo = [0,1] for i in 2 ... n { memo.append(memo[i-1] + memo[i-2]) } return memo[n] } 

(Note that your memo array is set to an initial value [0, 1] for each function call, so the values โ€‹โ€‹are not really โ€œmemoized.โ€ Without memoization, you do not need an array, it would be enough for the last two numbers to calculate the next. )

+6
source share

As it turns out, the i variable will always be equal to the count memoizing of the array, so you can just use this as a loop condition:

 func fibonacci(n: Int) -> Int { var memo = [0,1] while n >= memo.count { memo.append(memo[memo.count-1] + memo[memo.count-2]) } return memo[n] } 

Alternatively, you can express the loop as a recursive function:

 func fibonacci(n: Int) -> Int { var memo = [0,1] func rec(i: Int) -> Int { if i >= memo.count { memo.append(rec(i-2) + rec(i-1)) } return memo[i] } return rec(n) } 

Indeed, although if is the best solution here. Range does not allow the end to be smaller than the beginning of the design. Additional line for:

 func fibonacci(n: Int) -> Int { if n < 2 { return n } var memo = [0,1] for i in 2...n { memo.append(memo[i-1] + memo[i-2]) } return memo[n] } 

It is readable and understandable. (In my opinion, the code above is better than the for ;; version)

+2
source share

@Marc the answer is great: fooobar.com/questions/833731 / ...

But the syntax of the step is too long for frequent use, so I made it a little more enjoyable for regular applications I ++ ...

 extension Strideable { @warn_unused_result public func stride(to end: Self) -> StrideTo<Self> { return stride(to: end, by: 1) } } extension Strideable { @warn_unused_result public func stride(thru end: Self) -> StrideThrough<Self> { return stride(through: end, by: 1) } } 

So use like this:

 for i in startPos.stride(to: endPos) { print("pos at: \(i)") } 
0
source share

All Articles