Here is a recursive function that will take a list of primes, and return your tuple where your sum is greater than k:

let findLimit k l =
let rec inner p s l2 =
if s > k then
(p, s)
else
inner (List.hd l2) (s + 1.0 / (float)(List.hd l2)) (List.tl l2)
inner 0I 0.0 l

To use your seq of primes where the limit is 1.5, you would say

findLimit 1.5 (Seq.to_list primes);;

By on 2/19/2009 4:33 PM ()

Hi,
I think your original solution is just fine. Unfortunatelly, doing this kind of things with sequences is a bit difficult. Using lists won't work in this scenario, because the sequence of primes is presumably infinite, so it cannot be converted to a list (F# isn't lazy, so it would have an infinite length!)

The "seq" type is quite imperative under the hood. You could use it directly in the implementation like this:

1
2
3
4
5
6
7
8
9
 
let primeseries = seq {
  let en = primes.GetEnumerator()
  let rec loop(x) = seq {
    if en.MoveNext() then 
      let x = x + 1.0/float(p)
      yield en.Current, x
      yield! loop(x) }
  yield! loop(0) }

...but I don't think this is better. The "MoveNext" method moves the enumerator to the next prime number in the sequence, so it performs a side effect there. Also the use of "yield!" may be quite inefficient, so I think your existing solution is good :-).

The best advice would be to write some higher order function that would allow you to solve this kind of problems, so you don't have to use mutation explicitly. For example you may write something like this:

1
2
3
4
5
6
7
 
let mapWithState f initial input = 
  seq { let x = ref initial
        for p in input do
          let el, nextState = f (!x) p
          x := nextState
          yield el }

Now I guess it should be possible to solve the problem using this function...

By on 2/19/2009 5:04 PM ()

I like the mapWithState function.

I am forgetting that at the end of the day everything ends up imperative under the hood (it has to end up as machine code at some stage after all!)

I suppose, the trick is to have enough tools in the toolbag to write purely functional application code even if the tools themselves may be written less elegantly.

Presumably I could implement mapWithState as a type extension to Seq could I? Just to make it obvious that it is a generic tool.

Thanks to the other other posts using LazyLists. I can't say I fully understand the code snippets so need to do some reading up and experimenting. They look quite powerful. I'll end up learning something new which was the point of the original exercise.

Cheers everyone.

By on 2/20/2009 5:46 AM ()

Don't worry, I didn't understand them either a few weeks ago, until I posted about a similar problem and someone helped me out.

The general idea is that you don't specify the elements in the list. You specify the first item, and then a function to get the n'th item from the n-1'th item, which is recursive in terms of the list itself. The internals handle everything else. An easier example would be the list of all numbers:

let rec all_integers_starting_from n = LazyList.consf n (fun () -> all_integers_starting_from (n+1))

let all_natural_numbers = all_integers_starting_from 1

In this example, it's easy to see that the first time it simply puts 1 onto the list, and additionally it puts a function on that, when you try to access the second element, it calls that function with the current value. When it calls that function, it sticks 2 onto the list, followed by another function. This repeats forever. Each time a new element is stuck onto the list, its value is remembered, so that accessing it later will always be cached.

In the case of primes, we force 2 and 3 onto the list because 2 is a special case (even), and we normally we get "candidate" primes by adding 2 to the previous number. So forcing 2 onto the list, we have a function which is "all_primes_starting_from n" which, given n, finds the next prime number p and just returns a LazyList.consf p (fun () -> all_primes_starting_from (p+2)).

Since the functions are all mutually recursive, they can use each other at will and the implementation handles the details. If you look at the LazyList implementation I posted, it keeps calling all_primes_after, which delegates to a function which returns the next prime (i.e. it returns an integer). This function just keeps adding 2 and then testing the number for primality, but the way it tests the number for primality in the is_prime function is by using the previously generated primes (all the functions are specified with "let ... and ... and ..."

Also, as mentioned due to the caching, say you're somewhere in the 1 billions and you're checking if a number n is prime. Normally you would go through all odd numbers up to the square root of n looking for one that divided. But ideally you only want to check _primes_ up to sqrt(n). The LazyList provides an easy solution to this. If you've already examined the primes before this number n, then their primality has already been determined and the prime numbers are cached in the list. Thus simply walking the list will return every prime up to sqrt(n) in linear time, and you can ignore all the other numbers

By on 2/20/2009 6:32 AM ()

I just thought I mentioned a few things about the prime number generator. You can do a little bit better than only considering odd numbers as prime candidates. All prime numbers greater than 3 are of the form 6n +/- 1, for some n >= 1. Hence your candidates sequence could be 5, 7, 11, 13, 17, 19, 23, 25, ... This works because if you consider integers modulo 6, then 0, 2, 3, and 4 are divisible by 2 or 3, leaving only 1 and 5 as potentially prime numbers.

If you want you can take this a step further say that all primes greater than 5 are equal to 1, 7, 11, 13, 17, 19, 23 or 29 modulo 30. But that might be taking it a bit too far. :-)

By on 2/20/2009 10:26 PM ()

I see your point about finite lists, but can't I get around that with LazyLists?

For example, we start with the original isPrime x:bigint -> bool, then we just write

let rec getNextPrime (x:bigint) =
if (isPrime x) then
x
else
getNextPrime (x + 1I)

and build a LazyList of primes with

let ll = LazyList.unfold (fun x -> Some (x, getNextPrime (x + 1I))) 2I

and then change the "get the tuple" function to

let findLimit k l =
let rec inner p s l2 =
if s > k then
(p, s)
else
match l2 with
| LazyList.Cons(h, t) -> inner (h) (s + 1.0 / (float)(h)) (t)
| LazyList.Nil -> failwith "WTF?"
inner 0I 0.0 l

?

By on 2/19/2009 6:12 PM ()

Hi.

I had an identical problem a few weeks ago. Here is a lazy list implementation of the prime number sequence:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let rec smallest_prime_factor n = 
    let limit = System.Math.Sqrt(float(n)) |> int
    let rec smallest_prime_factor primes = 
        match primes with
        | LazyList.Cons(p,_) when (p>limit) -> n
        | LazyList.Cons(p,_) when (n%p=0) -> p
        | LazyList.Cons(_,tail) -> smallest_prime_factor tail
        | _ -> failwith "Invalid prime sequence!"
    smallest_prime_factor primes
and is_prime n = ((smallest_prime_factor n) = n)
and next_prime_after n = 
    assert(n%2=1)
    let candidate = n+2
    if (is_prime candidate) then candidate
    else next_prime_after candidate
and all_primes_after n = 
    let next = next_prime_after n
    LazyList.consf next (fun () -> all_primes_after next)
and primes = LazyList.consf 2 (fun () -> LazyList.consf 3 ( fun() -> all_primes_after 3 ) )

The great thing about this strategy is that not only is there absolutely no mutation anywhere, but also you will never go through the same calculations twice for the same number. So while the first iteration might be slow, successive iterations will be very fast. Plus general prime number calculation is faster, because when you're testing a number for primality you have to generate smaller primes to attempt to find a factor. Using the lazy implementation, you cant even get to a prime p until you've determined the primality of all numbers less than p, but at that point all those values are remembered, so they're never tested again.

Now if you want to generate a sequence of primes tupled with the running sum just do this you can use the awesome LazyList.folds() function. Add the following to the end of the code above, as the very last line:

1
and prime_inverse_sums = LazyList.folds (fun (_,sum) p -> (p, sum + (1.0 / (p |> float)))) (0, 0.0) primes

That's it! Here's some output from FSI:

> LazyList.take 5 prime_inverse_sums |> Seq.to_list;;
val it : (int * float) list
= [(2, 0.5); (3, 0.8333333333); (5, 1.033333333); (7, 1.176190476); (11, 1.267099567)]
>

In my experience, the main reason to use a seq in the first place is to hide a mutable. If you want to _get rid_ of a mutable, try a LazyList before trying anything else.

By on 2/19/2009 5:52 PM ()
IntelliFactory Offices Copyright (c) 2011-2012 IntelliFactory. All rights reserved.
Home | Products | Consulting | Trainings | Blogs | Jobs | Contact Us | Terms of Use | Privacy Policy | Cookie Policy
Built with WebSharper