Another thing to be learned down the Haskell rabbit-hole: Thinking in infinites. Today someone posed a puzzle which I tried to solve in a straight-forward, recursive manner: Building a list of primes. The requested algorithm was plain enough:

Create a list of primes “as you go”, considering a number prime if it can’t be divided by any number already considered prime.

However, although my straightforward solution worked on discrete ranges, it couldn’t yield a single prime when called on an infinite range — something I’m completely unused to from other languages, except for some experience with the SERIES library in Common Lisp.

An incomplete solution

Looking similar to something I might have written in Lisp, I came up with this answer:

primes = reverse . foldl fn []
    where fn acc n
              | n `dividesBy` acc = acc
              | otherwise         = (n:acc)
          dividesBy x (y:ys)
              | y == 1         = False
              | x `mod` y == 0 = True
              | otherwise      = dividesBy x ys
          dividesBy x [] = False

But when I suggested this on #haskell, someone pointed out that you can’t reverse an infinite list. That’s when a light-bulb turned on: I hadn’t learned to think in infinites yet. Although my function worked fine for discrete ranges, like [1..100], it crashed on [1..].

So back to the drawing board, later to come up with this infinite-friendly version:

primes :: [Int] -> [Int]
primes = fn []
    where fn _ [] = []
          fn acc (y:ys)
              | y `dividesBy` acc = fn acc ys
              | otherwise         = y : fn (y:acc) ys

          dividesBy _ [] = False
          dividesBy x (y:ys)
              | y == 1         = False
              | x `mod` y == 0 = True
              | otherwise      = dividesBy x ys

Here the accumulator grows for each prime found, but returns results in order whose value can be calculated as needed. This time when I put primes [1..] into GHCi it printed out prime numbers immediately, but visibly slowed as the accumulator grew larger.

Tagged with:
 

6 Responses to Journey into Haskell, part 6

  1. Volkan YAZICI says:

    Better refer to the related SICP section[1] next time.

    [1] http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-24.html#%_sec_3.5.2

  2. Robert says:

    Wow that’s really easy to read.

  3. Dave Jones says:

    Hi John,

    First, I want to say I have enjoyed reading your writing for several years.

    You strike me as someone who would want to figure this all out on your own, but here is a link to a paper you may find interesting. The paper helped me learn some Haskell concepts better, and it explains a very nice prime number algorithm.

    http://www.cs.hmc.edu/~oneill/papers/Sieve-JFP.pdf

    Dave

  4. John Wiegley says:

    Thanks, Dave, I will definitely check that out. And although I do like to figure things out on my own at first, I also like to see what other minds have found, because often it leads down trails I never imagined.

  5. barak.a.pearlmutter.myopenid.com says:

    – So complex! Lazy birecursion,

    primes = 2:filter isPrime [3..]
    isPrime i = all ((/=0) . mod i) $ takeWhile ((

  6. John Wiegley says:

    Very cool. Now I just have to study it!

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Notify me of followup comments via e-mail. You can also subscribe without commenting.

Set your Twitter account name in your settings to use the TwitterBar Section.