February 4, 2008
@ 06:52 AM

Here:

Each new term in the Fibonacci sequence is generated by adding the previous two terms. By starting with 1 and 2, the first 10 terms will be:

1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...

Find the sum of all the even-valued terms in the sequence which do not exceed one million.

I started this just as I was learning about workflow expressions in F#. When I read the problem, it looked like a nail, and recursive workflow expressions felt like a hammer in my hand (see bottom half of page 244 of Expert F#.) I thought that the Fibonacci sequence would make a great recursive workflow expression.

  1. I first coded up the Fibonacci function itself:

        public class Problem_002_Tests
        {
            [Fact] private void Fibonacci_of_Neg_1_should_be_0()
            {
                Assert.Equal(0, Euler.Problem002.fibonacci(-1));
            }
            [Fact] private void Fibonacci_of_0_should_be_0()
            {
                Assert.Equal(0, Euler.Problem002.fibonacci(0));
            }
            [Fact] private void Fibonacci_of_1_should_be_1()
            {
                Assert.Equal(1, Euler.Problem002.fibonacci(1));
            }
            [Fact] private void Fibonacci_of_2_should_be_1()
            {
                Assert.Equal(1, Euler.Problem002.fibonacci(2));
            }
            [Fact] private void Fibonacci_of_3_should_be_2()
            {
                Assert.Equal(2, Euler.Problem002.fibonacci(3));
            }
            [Fact] private void Fibonacci_of_4_should_be_3()
            {
                Assert.Equal(3, Euler.Problem002.fibonacci(4));
            }
            [Fact] private void Fibonacci_of_17_should_be_1597()
            {
                Assert.Equal(1597, Euler.Problem002.fibonacci(17));
            }
        }

    ...

    #light

    namespace Euler

        module Problem002
        
        (* The fibonacci function *)
        let rec fibonacci n = 
            if n = 0 then 0
            elif n = 1 then 1
            elif n > 1 then fibonacci(n - 1) + fibonacci(n - 2)
            else 0
  2. Next, I made a sequence:

            [Fact] private void SeventeenthElementOfFibonacci_sequence_is_1597()
            {
                IEnumerable<int> sequence = Euler.Problem002.fibonacciSeq(1, 2000);
                Assert.Equal(1597, sequence.ElementAt(16));
            }

    ...

        (* A sequence expression of fibonacci terms less than l *)
        (* I wanted to use a simple recursive workflow expression,
           but where I used it in answer, it would try to apply Seq.filter to the 
           whole infinite sequence. Therefore, I had to introduce the limit 
           parameter *)

        let rec fibonacciSeq(n, l)
            seq {
                let term = fibonacci n
                if term <= l then
                    yield term
                    yield! fibonacciSeq(n + 1, l)
            }

     

    As the comment says, I wanted this to just be yield fibnoacci n; yield! fibonacciSeq n+1; but had to introduce the limit.

  3. Finally, I made an even predicate and filtered and folded the sequence into the answer:

            [Fact] private void Answer_should_be_1089154()
            {
                Assert.Equal(1089154, Euler.Problem002.answer());
            }

    ...

        let even_p n = 
            n % 2 = 0
        
        (* Too bad there is not something like Seq.filter that assumes the sequence
           is in order, and could therefore stop iterating when the filter condition
           was met. If there was, I wouldn't need the limit parameter here. *)
        
        let answer()
            fibonacciSeq(1, 1000000) 
            |> Seq.filter(even_p) 
            |> Seq.fold (+) 0

     

    I think there is something like Seq.filter that's delayed...I think it's called Seq.delay!? But I don't really understand it yet. If I did have something like that, I could just make the answer include an extra |> Seq.filter (fun n -> n < 1000000).

     

I've started to swing towards the dark side with respect to single letter variable names, but since I still have meaningful method names and structure I feel OK about it.

Again, this is way less elegant than what you can see in the Project Euler forums, but I got to play with sequence expressions more and learned a few more things.


 
Categories: