Search code examples
haskellf#sieve-algorithm

Haskell --> F#: Turner's Sieve


I was reading on different sieving algorithms when I stumbled upon a kind of improved version of the Sieve of Eratosthenes called Euler's Sieve. According to Wikipedia there is an implementation of an slightly different version of the idea (called Turner's Sieve) in Haskell.

Now I am trying to understand what exactly the code snippet given does and I think I've got it but now I wanted to translate the code into F# and have really no idea where to start. My main concern is that there doesn't seem to be a function to "substract" two sequences.

Here's the code:

import Data.OrdList (minus)

primes = euler [2..]
euler (p : xs) = p : euler (xs `minus` map (*p) (p : xs))

How would this be implemented in F#? Is it even possible?


Solution

  • If you want to calculate things like merges/differences of infinite lists like Haskell does, the LazyList type (found inside the F# power pack) springs to mind.

    It makes for very verbose code, like the translation below:

    #r "FSharp.PowerPack.dll"
    
    //A lazy stream of numbers, starting from x
    let rec numsFrom x = LazyList.consDelayed x (fun () -> numsFrom (x+1))
    
    //subtracts L2 from L1, where L1 and L2 are both sorted(!) lazy lists
    let rec lazyDiff L1 L2 =
        match L1,L2 with
            | LazyList.Cons(x1,xs1),LazyList.Cons(x2,xs2) when x1<x2 ->
                LazyList.consDelayed x1 (fun () -> lazyDiff xs1 L2)
            | LazyList.Cons(x1,xs1),LazyList.Cons(x2,xs2) when x1=x2 ->
                lazyDiff xs1 L2
            | LazyList.Cons(x1,xs1),LazyList.Cons(x2,xs2) when x1>x2 ->
                lazyDiff L1 xs2
            | _ -> failwith "Should not occur with infinite lists!"
    
    let rec euler = function
        | LazyList.Cons(p,xs) as LL ->
            let remaining = lazyDiff xs (LazyList.map ((*) p) LL)
            LazyList.consDelayed p (fun () -> euler remaining)
        | _ -> failwith "Should be unpossible with infinite lists!"
    
    let primes = euler (numsFrom 2)
    

    with

    > primes |> Seq.take 15 |> Seq.toList;;
    val it : int list = [2; 3; 5; 7; 11; 13; 17; 19; 23; 29; 31; 37; 41; 43; 47]
    

    Note that I added two failwith clauses to keep the compiler from complaining about an incomplete match, even if we know that all lists in the calculation are (lazily) infinite.