HiveBrain v1.2.0
Get Started
← Back to all entries
patternswiftMinor

Swift replacement for C's for-loop

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
swiftloopforreplacement

Problem

I recently wrote an entry on my blog regarding unit testing using prime numbers as an example.

When I wrote the entry, I wrote my code keeping in minding that proposal SE-0007 has been accepted for Swift 3.0. That is, C-style for loops will no longer be available in Swift as of 3.0, so I want to completely discontinue using them.

That is to say, where I might have written something like this:

for(int divisor = 7; divisor * divisor <= value; divisor += 30) {


I was instead stuck with some real ugliness in my prime loop.

func isPrime(value: Int) -> Bool {
    if value < 2 { return false }
    if value % 2 == 0 { return value == 2 }
    if value % 3 == 0 { return value == 3 }
    if value % 5 == 0 { return value == 5 }
    if value == 7 { return true }

    var divisor = 7
    while divisor * divisor <= value {
        if value % divisor == 0 { return false }
        if value % (divisor + 4) == 0 { return false }
        if value % (divisor + 6) == 0 { return false }
        if value % (divisor + 10) == 0 { return false }
        if value % (divisor + 12) == 0 { return false }
        if value % (divisor + 16) == 0 { return false }
        if value % (divisor + 22) == 0 { return false }
        if value % (divisor + 24) == 0 { return false }
        divisor += 30
    }

    return true
}


I am satisfied that this code works as I want. I am also satisfied that is runs extraordinarily fast.

What I do not like about this is the very round-about way I have to achieve the same behavior a C-style for loop would have given me. In particular, it is the termination point that concerns me. I could easily get the same loop steps with the stride function:

for divisor in 7.stride(through: value, by: 30) {


But this doesn't allow for the same early stopping point:

divisor * divisior <= value


And making the first line of the loop check this isn't much better:

```
for divisor in 7.stride(through: value, by: 30) {
if divisor * divi

Solution

Python also has no C-style for-loop. A lot of Python novices write clumsy while-loops as a result. However, once you learn the Pythonic idioms for looping, you'll wonder why other languages aren't more like Python.

The trick is to get to know…

  • the range() built-in function, which looks a lot like the stride(through: by:) that you suggested



  • the enumerate() built-in function, for when you need to iterate over a list and also increment a counter at the same time



  • the itertools library for more complex situations



If I had to translate your Swift code to Python, I would do it using itertools.count() to generate the infinite sequence [7, 37, 67, 97, …], and itertools.takewhile() to terminate it.

from itertools import count, takewhile

def is_prime(value):
    if value < 2: return False
    if value % 2 == 0: return value == 2
    if value % 3 == 0: return value == 3
    if value % 5 == 0: return value == 5
    if value == 7: return True

    for divisor in takewhile(lambda d: d * d <= value, count(7, 30)):
        if value % divisor == 0: return False
        for offset in [4, 6, 10, 12, 16, 22, 24]:
            if value % (divisor + offset) == 0: return False
    return True


Kevin Ballard has posted a proposal on the swift-evolution mailing list to introduce a takeWhile() to the standard library. I think that a rich library for manipulating sequence types would make a good replacement for C-style for-loops.

Code Snippets

from itertools import count, takewhile

def is_prime(value):
    if value < 2: return False
    if value % 2 == 0: return value == 2
    if value % 3 == 0: return value == 3
    if value % 5 == 0: return value == 5
    if value == 7: return True

    for divisor in takewhile(lambda d: d * d <= value, count(7, 30)):
        if value % divisor == 0: return False
        for offset in [4, 6, 10, 12, 16, 22, 24]:
            if value % (divisor + offset) == 0: return False
    return True

Context

StackExchange Code Review Q#118445, answer score: 6

Revisions (0)

No revisions yet.