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.
-
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
-
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.
-
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.