41

I have a for loop that checks if a number is a factor of a number, then checks if that factor is prime, and then it adds it to an array. Depending on the original number, I will get an error saying

fatal error: Can't form range with end < start

This happens almost every time, but for some numbers it works fine. The only numbers I have found to work with it are 9, 15, and 25.

Here is the code:

let num = 16 // or any Int
var primes = [Int]()

for i in 2...(num/2) {

    if ((num % i) == 0) {
        var isPrimeFactor = true

        for l in 2...i-1 {
            if ((i%l) == 0) {
                isPrimeFactor = false;
            }//end if
        }//end for

        if (isPrimeFactor == true) {
            primes.append(i)
        }//end if

    }//end if

}//end for
alexwlchan
  • 5,699
  • 7
  • 38
  • 49
lagoon
  • 6,417
  • 6
  • 23
  • 30

7 Answers7

72

Swift 5

If you need a loop with dynamic value-range, I suggest that using stride(to:by:) instead of ..< or ...

Basically ..< or ... will be crashed if start_index > end_index.

This will be crash:

let k = 5
for i in 10...k { print("i=\(i)") }
for i in 10..<k { print("i=\(i)") }

How to fix:

let k = 5
for i in stride(from: 10, through: k, by: 1) { print("i=\(i)") }
for i in stride(from: 10, to: k, by: 1) { print("i=\(i)") }

NOTE:

The code above won't print out anything, but it won't be crash when execution.

Also, if you want to stride from a higher number to a lower number then the by parameter needs to be changed to a negative number.

Reference:

nahung89
  • 7,745
  • 3
  • 38
  • 40
  • 8
    "by" parameter must be (-1) or nothing will be printed – igrrik Jul 11 '16 at 10:56
  • 3
    @igrrik: Please read carefully. We are talking about the fatal error cause by ..< and how to fix it. Run two block codes above separate and you will see. – nahung89 Jul 12 '16 at 01:18
9

In your second loop, i will always start at 2, which means you're looping from 2...1

chris
  • 4,840
  • 5
  • 35
  • 66
6

SWIIFT 4

The best way to go is to use stride as by this documentation page: Generic Function stride(from:to:by:)

for i in stride(from: 10, through: 5, by: -1) { print(i) }

and stride through if you want to include the lowerBound: Generic Function stride(from:through:by:)

Wissa
  • 1,444
  • 20
  • 24
5

Both ClosedRange and CountableRange throw this error unless start <= end. One problem with using stride(from:to:by) is that it returns a Strideable and not a Range, which doesn't work with the "is in range" operator ~=. To handle these cases, I use an extension which gives me a bounds-safe range, where invalid ranges become an empty range:

extension Int {
  func until(_ end: Int) -> CountableRange<Int> {
    return self <= end ? self..<end : self..<self
  }

  func through(_ end: Int) -> CountableRange<Int> {
    return self <= end ? self..<(end + 1) : self..<self
  }
}

Both return CountableRange so that an invalid through range becomes an empty range. (Side note: the name until is chosen because this "safe" range behaves the same as until ranges in Kotlin/Android).

Usage:

for i in 0.until(3) { /* do something */ }

let printRange = { (range: Range<Int>) -> Void in
  for i in range {
    print(i, terminator: " ")
  }
  print()
}
printRange(0.until(3))  // prints "0 1 2"
printRange(0.until(0))  // prints ""
printRange(3.until(0))  // prints ""
printRange(0.through(3))  // prints "0 1 2 3"
printRange(0.through(0))  // prints "0"
printRange(3.through(0))  // prints ""
print(0.until(1) ~= -1)  // prints "false"
print(0.until(1) ~= 0)  // prints "true"
print(0.until(1) ~= 1)  // prints "false"
print(0.until(0) ~= 0)  // prints "false"
print(1.until(0) ~= 0)  // prints "false"
print(0.through(1) ~= -1)  // prints "false"
print(0.through(1) ~= 0)  // prints "true"
print(0.through(1) ~= 1)  // prints "true"
print(0.through(0) ~= 0)  // prints "true"
print(1.through(0) ~= 0)  // prints "false"
Bryan W. Wagner
  • 867
  • 1
  • 12
  • 13
2

If you want to use range where upperBound < lowerBound, you can add reversed() to it.

for eg: for number in (0...highestPossibleNumber).reversed()

Ali
  • 2,427
  • 22
  • 25
0

Using a simple solution you can make for loop reverse using Stride and -



func reverseArray(oldarray: [String]) -> [String] {
        var newArray = [String]()
        let len = oldarray.count
        for i in stride(from: len - 1, through: 0, by: -1)
        {    newArray.append(oldarray[i])
            print(i)
        }
        return newArray
    }

input :  print(reverseArray(oldarray: ["World", "Hello"]))

output : ["Hello", "World"]
Ullas Pujary
  • 349
  • 4
  • 14
0

The easiest solution is to apply ".reversed()" to your array. Example: array.reversed()