Showing posts with label Fibonacci. Show all posts
Showing posts with label Fibonacci. Show all posts

Thursday, September 30, 2021

Coding Practice: Learning Rust with Fibonacci Numbers

In my exploration of Rust, I built an application that calculates Fibonacci numbers (this was a suggestion from the end of Chapter 3 of The Rust Programming Language by Steve Klabnik and Carol Nichols).

It helped me learn a bit more about the language and environment.
  • for loops
  • Statements vs. expressions
  • Function returns (expressions)
  • checked_add to prevent overflow
  • Option enum (returned from checked_add)
  • Pattern matching on Option
  • Result enum (to return error rather than panic)
  • .expect with Result
  • Pattern matching on Result
So let's walk through this project.

The code is available on GitHub: https://github.com/jeremybytes/fibonacci-rust, and branches are set up for each step along the way. We will only be looking at the "main.rs" file in each branch, so all of the links will be directly to this file.

Fibonacci Numbers

The task is to calculate the nth Fibonacci number. The Fibonacci sequence is made by adding the 2 previous number in the sequence. So the sequence starts: 1, 1, 2, 3, 5, 8, 13, 21, 34. The 7th Fibonacci number (13) is the sum of the previous 2 numbers (5 and 8).

For our application, we will create a function to generate the nth Fibonacci number based on an input parameter. We'll call this function with multiple values and output the results.

Step 1: Creating the Project

Branch: 01-creation
The first step is to create the project. We can do this by typing the following in a terminal:
    cargo new fib
This will create a new folder called "fib" along with the Rust project file, a "src" folder to hold the code, and a "main.rs" file (in src) which is where we will be putting our code.

The "main.rs" file has placeholder code:
    fn main() {
        println!("Hello, world!");
    }
But we can use "cargo run" to make sure that everything is working in our environment.
    C:\rustlang\fib> cargo run
       Compiling fib v0.1.0 (C:\rustlang\fib)
        Finished dev [unoptimized + debuginfo] target(s) in 0.63s
         Running `target\debug\fib.exe`
    Hello, world!
Going forward, I'll just show the application output (without the compiling and running output).

Step 2: Basic Fibonacci

Branch: 02-basic
Now that we have the shell, let's create a function to return a Fibonacci number. Here's the completed function:
    fn fib(n: u8) -> u64 {
        let mut prev: u64 = 0;
        let mut curr: u64 = 1;
        for _ in 1..n {
            let next = prev + curr;
            prev = curr;
            curr = next;
        }
        curr
    }
There are several interesting bits here. Let's walk through them.

Declaring a Function
Let's start with the function declaration:
    fn fib(n: u8) -> u64 {

    }
The "fn" denotes that this is a function. "fib" is the function name. "n: u8" declares a parameter called "n" that is an unsigned 8-bit integer. And the "u64" after the arrow declares that this function returns an unsigned 64-bit integer.

When declaring parameters and return values for functions, the types are required. Rust does use type inference in some places (as we'll see), but function declarations need to have explicit types.

Declaring Variables
Next, we have some variables declared and assigned:
    let mut prev: u64 = 0;
    let mut curr: u64 = 1;
"let" declares a variable.

By default, variables are immutable. This means that once we assign a value, we cannot change it. For these variables, we use "mut" to denote that they are mutable. So we will be able to change the values later.

The variable names are "prev" and "curr". These will hold the "previous number" and the "current number" in the sequence.

The ": u64" declares these as unsigned 64-bit integer values. Fibonacci numbers tend to overflow very quickly, so I used a fairly large integer type.

Finally, we assign initial values of 0 and 1, respectively.

Looping with "for"
There are several ways to handle the loop required to calculate the Fibonacci number. I opted for a "for" loop:
    for _ in 1..n {

    }
"1..n" represents a range from 1 to the value of the incoming function argument. So if the argument is "3", this represents the range: 1, 2, 3.

The "for" statement will loop once for each value in the range. In this case the "_" denotes that we are not using the actual range value inside the loop. All we really need here is to run the loop 3 times. All of the calculation is done inside the loop itself.

Implicit Typing
Inside the "for" loop we do our calculations:
    let next = prev + curr;
    prev = curr;
    curr = next;
This creates a new variable called "next" inside the loop and assigns it the sum of "prev" and "curr". A couple of things to note. First, this variable is immutable (so we do not have the "mut" keyword). The value is assigned here and then it is not changed. Second, the "next" variable is implicitly typed. Instead of having a type declaration, it is set based on what is assigned to it. Since we are assigning the sum of two u64 values, "next" will also be a u64.

The next two lines update the "prev" and "curr" values. We needed to mark them as mutable when we declared them so that we could update them here.

This is a fairly naïve way of calculating Fibonacci numbers. If you'd like to see more details on how the calculation works, you can take a look at this article: TDDing into a Fibonacci Sequence with C#.

Statements vs. Expressions
The last line of the function is a bit interesting:
    curr
This returns the current value ("curr") from the function.

Rust does not use a "return" keyword to return a value. Instead, the last expression in a function is what is returned. (As a side note, this is similar to how F# works.)

What's the difference between a statement and an expression? A statement does some type of work; an expression returns a value.

In Rust, a statement ends with a semi-colon, and an expression does not end with a semi-colon. To make things more interesting, these are often combined.

Let's take a look back at a line of code:
    let next = prev + curr;
Overall, this is a statement: it declares and assigns a value to a variable called "next". And it ends with a semi-colon.
    prev + curr
"prev + curr" is an expression that returns the result of adding 2 values. So we really have a statement that includes an expression. (We can technically break this down further, but we won't do that here.)

So, let's get back to the return value of the function. The "fib" function returns a u64 value. The last expression in the function is:
    curr
It is important to note that this line does not end with a semi-colon. Because of this, the value of the "curr" variable (which is a u64) is returned for this function.

Because of my coding history, I'm used to putting semi-colons at the end of lines. So I'm sure that I'll mess this up many times before I get used to it. If you get an error that says a function is returning "()" instead of a particular type, it probably means that there's a semi-colon at the end of the expression you meant to return.

Here's the full function:
    fn fib(n: u8) -> u64 {
        let mut prev: u64 = 0;
        let mut curr: u64 = 1;
        for _ in 1..n {
            let next = prev + curr;
            prev = curr;
            curr = next;
        }
        curr
    }
Using the "fib" Function
Now that we have a function that returns a Fibonacci number, it's time to update the "main" function to use it.
    fn main() {
        println!("Fibonacci 1st = {}", fib(1));
        println!("Fibonacci 2nd = {}", fib(2));
        println!("Fibonacci 3rd = {}", fib(3));
        println!("Fibonacci 4th = {}", fib(4));
        println!("Fibonacci 5th = {}", fib(5));
    }
This uses the "println!" macro to output a string to the standard output. On each line, the set of curly braces represents a placeholder in the string. So in the first statement, the curly braces will be replaced by the value that comes back from calling "fib(1)".

So let's run and check the output:
    Fibonacci 1st = 1
    Fibonacci 2nd = 1
    Fibonacci 3rd = 2
    Fibonacci 4th = 3
    Fibonacci 5th = 5
It works!

Well, it mostly works. We'll see a shortcoming in a bit.

Step 3: Testing More Values

Branch: 03-mainloop
Before looking at where we have a problem in the "fib" function, let's make it easier to test for different values. For this, we'll add an array of numbers to test, and then loop through them.

Here's an updated "main" function:
    fn main() {
        let nths = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

        for nth in nths {
            println!("Fibonacci {} = {}", nth, fib(nth));
        }
    }
Creating an Array
The first line sets up an array called "nths" and initializes it with the values of 1 through 10. I use this name because our original task was to calculate the "nth" Fibonacci number. This is a collection of all the ones we want to calculate.

We're using type inference to let the compiler pick the type for "nths". In this case, it determines that it is an array with 10 elements of type u8. It decides on u8 because the values are used as arguments for the "fib" function, and that takes a u8.

As an interesting note, if you comment out the "println!" statement, the "nths" variable is an array with 10 elements of type i32 (a signed 32-bit integer). This is the default integer type.

Type inference works as long as it can be determined at compile time. If it cannot be determined at compile time, then an explicit type needs to be added.

Another "for" Loop
We use a "for" loop to go through the array. Instead of discarding the value from the "for" loop (like we did above), we capture it in the "nth" variable.

Inside the loop, we have a "println!" with 2 placeholders, one for the loop value and one for the result of the "fib" function.

Here's what that output looks like:
    Fibonacci 1 = 1
    Fibonacci 2 = 1
    Fibonacci 3 = 2
    Fibonacci 4 = 3
    Fibonacci 5 = 5
    Fibonacci 6 = 8
    Fibonacci 7 = 13
    Fibonacci 8 = 21
    Fibonacci 9 = 34
    Fibonacci 10 = 55
And now we can more easily test values by adding to the array.

Overflow!
As I noted at the beginning, Fibonacci sequences tend to overflow pretty quickly (they increase the value by half for each item). We can see this by adding a "100" to our array.
    let nths = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100];
Here's the output when we run with these values:
    Fibonacci 1 = 1
    Fibonacci 2 = 1
    Fibonacci 3 = 2
    Fibonacci 4 = 3
    Fibonacci 5 = 5
    Fibonacci 6 = 8
    Fibonacci 7 = 13
    Fibonacci 8 = 21
    Fibonacci 9 = 34
    Fibonacci 10 = 55
    thread 'main' panicked at 'attempt to add with overflow', src\main.rs:13:20
    note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
    error: process didn't exit successfully: `target\debug\fib.exe` (exit code: 101)
This creates a "panic" in our application, and it exits with an error.

With a little experimentation, we will find that the 93rd Fibonacci number is fine, but the 94th will overflow the u64 value.

Checking for Overflow

Branch: 04-checkedadd
Now we can work on fixing the overflow. Our problem is with this line:
    let next = prev + curr;
If "prev" and "curr" are near the upper limits of the u64 range, then adding them together will go past that upper limit.

Some other languages will "wrap" the value (starting over again at 0). Rust will generate an error instead (in the form of a panic). If you do want to wrap the value, Rust does offer a "wrapped_add" function that does just that.

But we do not want to wrap, we would like to catch the error and give our users a better experience.

checked_add
Instead of using the default "+" operator, we can use the "checked_add" function. Here is that code:
    let result = prev.checked_add(curr);
"checked_add" does not panic if the value overflows. This is because it uses the Option enum.

Option Enum
The Option enum lets us return either a valid value or no value. "Some<T>" is used if there is a valid value, otherwise "None" is used.

For example, let's say that "prev" is 1 and "curr" is 2. The "result" would be "Some(3)".

If "prev" and "curr" are big enough to cause an overflow when added together, then "result" would be "None".

Pattern Matching
The great thing about having an Option as a return type is that we can use pattern matching with it.

Here is the inside of the updated "for" loop:
    let result = prev.checked_add(curr);
    match result {
        Some(next) => {
            prev = curr;
            curr = next;
        }
        None => {
            curr = 0;
            break;
        }
    }
The "match" keyword sets up the pattern matching for us.

The first "arm" has "Some(next)" as the pattern. The "next" part lets us assign a name to the value that we can use inside the block. In this case, "next" will hold the same value that it did in the earlier version ("prev" + "curr"), so inside the block, we can assign the "prev" and "curr" values like we did before.

The second "arm" has "None" as the pattern. This will be used if there is an overflow. If there is an overflow, then we set the "curr" variable to 0 and then break out of the "for" loop.

Here is the updated "fib" function:
    fn fib(n: u8) -> u64 {
        let mut prev: u64 = 0;
       let mut curr: u64 = 1;
        for _ in 1..n {
            let result = prev.checked_add(curr);
            match result {
                Some(next) => {
                    prev = curr;
                    curr = next;
                }
                None => {
                    curr = 0;
                    break;
                }
            }
        }
        curr
    }
Here's an updated array to test valid values and overflow values:
    let nths = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 90, 91, 92, 93, 94, 95, 96];
And here's the output:
    Fibonacci 1 = 1
    Fibonacci 2 = 1
    Fibonacci 3 = 2
    Fibonacci 4 = 3
    Fibonacci 5 = 5
    Fibonacci 6 = 8
    Fibonacci 7 = 13
    Fibonacci 8 = 21
    Fibonacci 9 = 34
    Fibonacci 10 = 55
    Fibonacci 90 = 2880067194370816120
    Fibonacci 91 = 4660046610375530309
    Fibonacci 92 = 7540113804746346429
    Fibonacci 93 = 12200160415121876738
    Fibonacci 94 = 0
    Fibonacci 95 = 0
    Fibonacci 96 = 0
Setting "curr" to 0 is not a great way to handle our error state. For now, it is fine because it gets rid of the panic, and our application keeps running.

Next up, we'll work on getting a real error that we can handle.

Using the Result Enum

Branch: 05-result
In the last article, I wrote a bit about my first impressions of error handling in Rust: Initial Impressions of Rust. A big part of that involves the Result enum.

Similar to the Option enum, the Result enum represents exclusive states. For Result, the options are "Ok" and "Err". Each of these can have their own type.

To update our "fib" function to return a Result, we'll need to make 2 updates.

Updating the Function Declaration
First, we'll need to update the signature of the function to return a Result. Here's the new signature:
    fn fib(n: u8) -> Result<u64, &'static str> {

    }
The "Result" enum has 2 generic type parameters. The first represents the type for the "Ok" value; the second represents the type for the "Err".

In this case, the "Ok" will be a u64.

The "Err" is a bit more confusing. We want to return a string, but if we try to use just "str", we get an error that the compiler cannot determine the size at compile time. And as we've seen, Rust needs to be able to determine things at compile time.

Instead of using "str", we can use "&str" to use the address of a string (Rust does use pointers; we won't talk too much about them today). The address is a fixed size, so that gets rid of the previous error. But we get a new error that there is a "missing lifetime specifier".

UPDATE Technical Note: '&str' is a string slice. This allows the Err to borrow the value of the string without taking ownership of it. (I've learned more about ownership since I wrote this article. It's pretty interesting.)

The good news is that the error also gives you a hint to consider using the "static" lifetime with an example.

I'm using Visual Studio Code with the Rust extension, so I get these errors and hints in the editor. But these same messages show up if you build using "cargo build".

Returning a Result
Now that we've updated the function signature, we need to actually return a Result. We can do this with some pattern matching.

Replace the previous expression at the end of the function:
    curr
with a "match":
    match curr == 0 {
        false => Ok(curr),
        true => Err("Calculation overflow")
    }
This looks at the value of the "curr" variable and compares it to 0. (Again, this isn't the best way to handle this, but we'll fix it a bit later).

If "curr" is not 0 (meaning there is a valid value), then we hit the "false" arm and return an "Ok" with the value.

If "curr" is 0 (meaning there was an overflow), then we hit the "true" arm and return an "Err" with an appropriate message.

Here's the updated "fib" function:
    fn fib(n: u8) -> Result<u64, &'static str> {
        let mut prev: u64 = 0;
        let mut curr: u64 = 1;
        for _ in 1..n {
            let result = prev.checked_add(curr);
            match result {
                Some(next) => {
                    prev = curr;
                    curr = next;
                }
                None => {
                    curr = 0;
                    break;
                }
            }
        }
        match curr == 0 {
            false => Ok(curr),
            true => Err("Calculation overflow")
        }
    }
Side Note: In thinking about this later, I could have done the pattern matching more elegantly. I started with an if/else block (which is why I'm matching on a boolean value). But we could also write the pattern matching to use the "curr" value directly:
    match curr {
        0 => Err("Calculation overflow"),
        _ => Ok(curr),
    }
This is more direct (but not really less confusing since 0 is a magic number here). Now the match is on the "curr" value itself. If the value is "0", then we return the Err. For the second arm, the underscore represents a catch-all. So if the value is anything other than "0", we return "Ok". Notice that I did have to reverse the order of the arms. The first match wins with pattern matching, so the default case needs to be at the end.

Both of these matches produce the same results. We won't worry about them too much because we'll be replacing this entirely in just a bit.

But since we changed the return type, our calling code needs to be updated.

Using ".expect"
One way that we can deal with the Result enum is to use the "expect()" function.

Here is the updated code from the "main" function:
    println!("Fibonacci {} = {}", nth, fib(nth).expect("Fibonacci calculation failed"));
After the call to "fib(nth)", we add an ".expect()" call and pass in a message.

"expect()" works on a Result enum. If the Result is "Ok", then it pulls out the value and returns it. So if there is no overflow, then the expected Fibonacci number is used for the placeholder.

But if Result is "Err", then "expect" will panic. That's not exactly what we want here, but this gets us one step closer.

With the "expect" in place, here is our output:
    Fibonacci 1 = 1
    Fibonacci 2 = 1
    Fibonacci 3 = 2
    Fibonacci 4 = 3
    Fibonacci 5 = 5
    Fibonacci 6 = 8
    Fibonacci 7 = 13
    Fibonacci 8 = 21
    Fibonacci 9 = 34
    Fibonacci 10 = 55
    Fibonacci 90 = 2880067194370816120
    Fibonacci 91 = 4660046610375530309
    Fibonacci 92 = 7540113804746346429
    Fibonacci 93 = 12200160415121876738
    thread 'main' panicked at 'Fibonacci calculation failed: "Calculation overflow"', src    \main.rs:5:53
    note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
    error: process didn't exit successfully: `target\debug\fib.exe` (exit code: 101)
Because we get a panic when trying to fetch #94, the application halts and does not process the rest of the values (95 and 96).

If we look at the output, we see both of the messages that we added. "Fibonacci calculation failed" is what we put into the "expect", and "Calculation overflow" is what we put into the "Err" Result.

But I'd like to get rid of the panic. And we can do that with more pattern matching.

Matching on Result

Branch: 06-matchresult
Just like we used pattern matching with Option in the "fib" function, we can use pattern matching with Result in the "main" function.

Pattern Matching
Here's the updated "for" loop from the "main" function:
    for nth in nths {
        match fib(nth) {
            Ok(result) => println!("Fibonacci {} = {}", nth, result),
            Err(e) => println!("Error at Fibonacci {}: {}", nth, e),
        }
    }
Inside the "for" loop, we match on the result of "fib(nth)".

If the Result is "Ok", then we use "println!" with the same string that we had before.

If the Result is "Err", then we output an error message.

Adding an Overflow Flag
The last thing I want to do is get rid of the "curr = 0" that denotes an overflow. Even though this works, it's a bit unclear. (And it can cause problems since some implementations of Fibonacci consider "0" to be a valid value.)

For this, we'll add a new variable called "overflow" to the "fib" function. Here's the completed function with "overflow" in place:
    fn fib(n: u8) -> Result<u64, &'static str> {
        let mut prev: u64 = 0;
        let mut curr: u64 = 1;
        let mut overflow = false;
        for _ in 1..n {
            let result = prev.checked_add(curr);
            match result {
                Some(next) => {
                    prev = curr;
                    curr = next;
                }
                None => {
                    overflow = true;
                    break;
                }
            }
        }
        match overflow {
            false => Ok(curr),
            true => Err("Calculation overflow")
        }
  }
A new mutable "overflow" variable is created and set to "false". Then if there is an overflow, it is set to "true". Finally, "overflow" is used in the final pattern matching to determine whether to return "Ok" or "Err".

Final Output
With these changes in place, here is our final output:
    Fibonacci 1 = 1
    Fibonacci 2 = 1
    Fibonacci 3 = 2
    Fibonacci 4 = 3
    Fibonacci 5 = 5
    Fibonacci 6 = 8
    Fibonacci 7 = 13
    Fibonacci 8 = 21
    Fibonacci 9 = 34
    Fibonacci 10 = 55
    Fibonacci 90 = 2880067194370816120
    Fibonacci 91 = 4660046610375530309
    Fibonacci 92 = 7540113804746346429
    Fibonacci 93 = 12200160415121876738
    Error at Fibonacci 94: Calculation overflow
    Error at Fibonacci 95: Calculation overflow
    Error at Fibonacci 96: Calculation overflow
This version no longer panics if there is an overflow. If we do have an overflow, it gives us an error message. And all of the values will get calculated, even if an overflow occurs in the middle.

Wrap Up

Calculating Fibonacci numbers is not a very complex task. But in this walkthrough, we got to use several features of Rust and understand a bit more about how the language works.
  • for loops
  • Statements vs. expressions
  • Function returns (expressions)
  • checked_add to prevent overflow
  • Option enum (returned from checked_add)
  • Pattern matching on Option
  • Result enum (to return error rather than panic)
  • .expect with Result
  • Pattern matching on Result
Quite honestly, I didn't expect to get this much out of this exercise. I've calculate Fibonacci sequences lots of times before. I was surprised about what I learned.

It's okay to do "simple" exercises. And it's okay to be surprised when they don't go quite as you expected.

Happy Coding!

Tuesday, April 25, 2017

Implementing a Fibonacci Sequence with Value Tuples in C# 7

I decided to do some experimentation with the Fibonacci Sequence. I wanted to see if I could implement it with value tuples in C# similar to the way that I had previously done with F#. And I wanted to see how similar or different they are.

Previously, I implemented a Fibonacci Sequence in C# by TDDing into the code. The code is pretty imperative, but the readability is okay (it could be better):


Even better, we have a set of passing unit tests. So we can safely do experimentation and know when we've messed something up.

Note: you can grab the code for this article on GitHub: jeremybytes/fibonacci-tdd. Both the TDD and value tuple steps are included as branches.

The Idea (from F#)
Here's the idea: last year I worked through the first 10 Euler problems to learn F# a bit better. In particular, Euler #2 uses the Fibonacci Sequence. The implementation that I ended up with used tuples, and I wondered if I could use the C# 7 value tuples to create something similar.

Here's the code from the F# implementation (you can see the whole thing here: Functional Practice: Euler Problem #2 in F#):


This uses a "fold" which runs the specified function for each item in the list and hangs on to an accumulator value. In this case, the accumulator is a tuple that is initialized to "(0,1)". Then for each item in the list, it will create a new tuple consisting of the second value of the original ("b") and the sum of the first and second values ("a + b"). For a deeper dive, check out that article.

My implementation in C# will be a little bit different from this. First, this function gets the "nth" Fibonacci value. So if I call this function "fibonacci 4", it will give me the single value of "3". Instead of returning the "nth" value, we want to create a sequence that iterates through all the values (and yes, the F# article does describe that a little later, but we'll take things one step at a time).

Switching to Value Tuples
Rather than using a "fold" (or the LINQ version "Aggregate"), we'll stick with the "while" loop to return the values as they are requested. But we still want to use the same type of tuple for the accumulator.

So the first step is to create a tuple variable that we can initialize to "(0,1)":


As mentioned in a prior article, we cannot simply use a value tuple out of the box (even though we're using Visual Studio 2017 and C# 7). The feature has to be brought in through a NuGet package. I'm still not happy about how this works today. As Thomas Levesque pointed out in the comments, the release order is kind of out of whack. Welcome to the new world.

Anyway, we just need to bring in the "System.ValueTuple" package:


And everything works:


One slight difference is that we have the initial values noted as "0L" and "1L". This tells the compiler that we want to use the "long" integer type, and it will create the correct tuple with 2 long values.

Now we just need to do the same math as in the F# function and return the result:


Just like the F# "fold" function above, we create a new tuple where the first element is the second element of the previous value, and the second element is the sum of the first and second elements.

Then we return the first element of the tuple (which is what the "fst" function does in the F# code).

The good news is that all of the tests still pass:


Great! That means we have a working implementation.

Improving Readability
The readability of this is not great. One thing we can do is give our tuple elements names. We'll use "current" and "next":


This makes it a little bit easier to keep track of the elements in our tuple. And it's also clear that we're returning the correct value "current".

And importantly, all of our tests still pass:


Is This Better?
It's great that I got to experiment some with value tuples (and sad that I ran into the issue trying to use them initially). But is this a better implementation?

Let's look at them side-by-side:

Original Implementation

With Tuples

Neither of these implementations is particularly readable. The original version suffers because we're using the previous, current, and next variables and swapping them from place to place. It's a bit confusing to figure out exactly what's happening.

The tuple version has kind of the same problem. We have eliminated one of the variables so now we only have two (current and next), but we're still doing a swap and math that isn't real clear.

Wrap Up
The fun thing about experimenting is that you're never quite sure what you'll find. In this case, it's interesting that we can implement the Fibonacci Sequence fairly easily using value tuples. But I don't think we've improved readability much. Less code is often good, but not if it obfuscates things.

I think I'd have to call these implementations a "draw" in their current state. They both have similar readability issues. But that just means that we can do some more experimentation to see what we can come up with.

Happy Coding!

Monday, April 24, 2017

TDDing into a Fibonacci Sequence in C#

A few days ago, I mentioned how I wanted to do a bit of experimentation with a Fibonacci Sequence implementation. Before I did my experimentation, I wanted a working sequence and a set of tests to validate that. This way I would know if my refactoring broke something. [Update: here's some of that experimentation.]

So let's TDD into a Fibonacci Sequence. You can grab the code from GitHub: jeremybytes/fibonacci-tdd. There are branches to go along with each step.

Initial Project
For the initial project, I created a console application and a class library to hold my tests. I figured that I could put my Fibonacci sequence class in the console application and then move it to its own project if needed.

We'll basically be looking at 2 files. The first is "FibonacciSequence.cs", and (as mentioned) this is in the console application:


Then we have the test, "FibonacciSequenceTests.cs":


Since we're going to be writing tests to make sure we've got the sequence right, I included the first 12 Fibonacci numbers. If you're not familiar with the Fibonacci sequence, each value is determined by adding the previous 2 values. So the 6th value (8) is determined by adding the previous values of 3 and 5.

The First Test
So let's write a test for the first element in the sequence. But before we do that, I'm going to do a bit of setup.

Since I want this to be a sequence (which means IEnumerable in the C# world), I'm going to stub out the interface in our production class:


I know that this is writing code before tests. But since we know we want a sequence, it makes sense to give ourselves a framework to hang our code on. Notice that even though I've added the code for the "IEnumerable<int>" interface, we haven't included any implementation. We'll write our test first.

And here's the first test:


This creates an instance of the sequence, pulls the first value, and then checks to see that it's "1".

This tests fails (as expected):


The reason for the failure is the "NotImplementedException" that gets thrown by our class.

So let's write the very simplest code possible to get this to pass:


The "yield return" will create an enumerator for us in the background so we don't have to deal with it explicitly. For more information on IEnumerable, you can check out this article series: Next, Please! A Closer Look at IEnumerable.

This code is a bit too simple. We know that it doesn't really fulfill our sequence needs. But it does get our first test to pass:


So let's move on to the next test.

Testing the 2nd Element
Now that we've got things set up, it should be easier to write additional tests. Lets set up a test for the 2nd element in the Fibonacci Sequence:


Testing the first element of a sequence is easy. Testing the second element is a bit trickier. What I did here was use the "Take" method (one of the awesome LINQ methods) to grab the first 2 items of the sequence. Then I take the "Last" value.

But there's a problem with this test. It passes:


Yikes. That's not good. When doing TDD, I expect the test to fail until I add proper implementation code. So what's the problem?

The "Take" method will grab up to the number of values requested. In our case, the sequence only returns a single item. But "Take" doesn't care; it grabs as much as it can. Then when we ask for the "Last" value, we end up getting the only one that's there, which is the first value.

Correcting the Test
When we have a test that passes when we expect it to fail, we've either got a problem with our code or a problem with our test. In this case, the problem is with our test. "Take" is not the appropriate LINQ method to use here. Fortunately, there's another LINQ method we can use: "ElementAt":


One thing to keep in mind is that "ElementAt" uses a 0-based index. So using "1" will get us the 2nd item in the sequence.

Now our test fails:


This is better. The reason for the failure is that there is no 2nd item so "ElementAt" throws an exception.

For this implementation, we'll do something very simple:


I'm feeling like this is too simple still. But I'm also thinking that we can get a little more info to let the method take shape a little more naturally before we think about refactoring.

This is enough to get the test to pass:


So let's keep moving.

Testing the 3rd Element
Next we'll write a test for the 3rd element, which we expect will be "2":


This test fails (as expected), so let's write a bit more code:


This gets our test to pass, but it's kind of stupid to keep writing the method this way. So, I'm going to to a bit of refactoring.

Refactoring to Something a Bit More Useful
Since I see a little bit of a pattern forming here, I'm going to add a "for" loop to the implementation:


I know that this won't work for the entire sequence. But it works for the tests that we have in place right now. We'll worry about fixing this in a bit, but not until our test cases call for it.

Parameterizing the Tests
It looks like we're writing the same test over and over again. This is where I look to see if we can parameterize the test. In this case, I think it will work pretty well.

Here's a new test:


I'm using NUnit, so I can set up test cases that get passed in as parameters. For more information, take a look at this article: Parameterized Tests with NUnit.

This will plug the values of the TestCases into the parameters of the test. So in the first case, it will use "0" for the "ElementAt" call (which is the first item in the sequence) and it will use "1" as the "expected" value in the assertion.

This lets us test the first 3 elements of the sequence with a single test. And our results show the tests are passing:


Notice that the results show the parameter values plugged in. This is really useful when one (or more) of these test cases fail.

Testing the 4th Element
Testing the 4th element of the sequence is as simple as adding another test case:


This test passes without any changes to our code. And that's okay. We expect the 4th element is "3", and that's what our code returns.

It's interesting to see that the simple "for" loop that we built in the code works for 3 elements in the sequence. But we'll see it break down with the next element.

Testing the 5th Element
Adding the test case for the 5th element creates a failing test. I won't show a screen shot, but the test just has "TestCase(4,5)" added.

Now we have to do a bit of math to really implement the Fibonacci Sequence:


Like the description of the Fibonacci Sequence, I add together the previous two values. A couple of local variable hang on to those values so they can be used to calculate the next item.

With this in place, all of our tests pass.

Refactoring
The tests are passing, but I see some dead code in the implementation. We are no longer using the indexer of the "for" loop. Since we don't need the indexer, we can swap the "for" loop for a "while" loop:


I'm never a huge fan of "while(true)", but it is a good way to create an infinite sequence.

The First 12 Elements
Since we've implemented the definition of the Fibonacci Sequence, we would expect that additional tests would pass. I set up the test cases for the first 12 elements:


And all of the tests pass:


Yay!

But there's a problem.

Overflow!
I've done a lot of experimentation with the Fibonacci Sequence. (I'm not sure why it intrigues me so much.) One thing I know about it is that it will overflow a 32-bit integer pretty quickly. How quickly?

Let's set up the console application to find out. We'll add code to our console application to print out the first 50 values of the Fibonacci Sequence.


And here's the output:


As we can see, something weird starts to happen at item #47. We overflow the standard integer and end up with a negative number (since the default 32-bit integer is signed).

Testing for Overflow
Now that we know where our problem is, we can create a test for it.


This test grabs elements #46 and #47 (remember, "ElementAt" is 0-based). Then we check to make sure that #47 is greater than #46.

Since this is the pivot-point of the 32-bit integer, element #47 give us a negative value, so it is *not* greater. This means our test fails.

Fixing Overflow
To fix the overflow, we'll change from using "int" (a 32-bit integer) to using a "long" (a 64-bit integer). The code is fairly easy to update at this point.


There's also one change we need to make in our tests. The "expected" parameter type needs to be changed to "long".


With this in place, we now have all of our tests passing (including the one that checks for overflow):


And we can see our console application is behaving as expected as well:


Wrap Up
This code isn't perfect. Our implementation returns an infinite sequence (because of the "while(true)"). But even our updated code isn't infinite. After a while we'll overflow the 64-bit integer as well. So we should probably add some checks in the code for overflow and end the sequence. But I'll leave that as an exercise for you.

We've reached our goal which is to have an implementation of the Fibonacci Sequence with a set of valid tests. Now that we have this in place, we can do some experimentation with the implementation.

And with the tests in place, we'll know immediately if our experimentation breaks something. In an upcoming article, we'll look at those experiments. [Update: here's a bit of that experimentation.]

Happy Coding!

Friday, January 31, 2014

Finding Elegance in Functional Programming

There is a certain elegance that I've found with my explorations with functional programming and Haskell. My plan is to take these learnings and apply them to the .NET world -- most likely with F#, but also by taking better advantage of the functional parts of C#.

I'm all about using the right tool for the job. This is why I get discouraged when I see zealots who say that we should always use a certain programming paradigm or a particular language. (You can see here for a rather humorous take on this: Why [Programming Language X] Is Unambiguously Better than [Programming Language Y]).

Euler Problems
I've talked about Euler problems previously. These are mathematical problems that really lend themselves to functional solutions. It's really easy to build up a solution by breaking the problem down into discrete pieces and adding the functionality step by step.

So, let's take a look at Euler problem #2. This is not very complicated, but it does have quite a few steps.
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, ... By considering the terms in the Fibonacci sequence whose values do not exceed four million, find the sum of the even-valued terms.
The first step of this problem is to generate a Fibonacci sequence. Lucky for us, we have a good function for that. We have the Fibonacci sequence itself:

fibs = 1 : 1 : zipWith (+) fibs (tail fibs)

And we have a separate function that uses a list comprehension to create a limited sequence:

testFibs = [x | x <- takeWhile (<= 21) fibs]

Let's combine these into a single function:

testFibs = [x | x <- takeWhile (<= 21) fibs]
  where
    fibs = 1 : 1 : zipWith (+) fibs (tail fibs)

We've rolled in the "fibs" definition into our "testFibs" function. The overall result is that we have a list comprehension that provides us with Fibonacci numbers as long as the values are less than or equal to 21. And here's the output:

[1,1,2,3,5,8,13,21]

Now, one issue with the Euler problem is that it defines the Fibonacci sequence a little bit differently from what we have here. Most Fibonacci sequence implementations start with 1 and 1 (resulting in 1, 1, 2, 3, 5, 8...) or with 0 and 1 (resulting in 0, 1, 1, 2, 3, 5, 8...). This example specifies starting with 1 and 2.

We technically don't need to account for this discrepancy because the problem also specifies that we want "even-valued terms", so the leading "1" would be excluded. But we can also update our "fibs" function to account for this:

testFibs = [x | x <- takeWhile (<= 21) fibs]
  where
    fibs = 1 : 2 : zipWith (+) fibs (tail fibs)

Which gives us this updated sequence: [1,2,3,5,8,13,21]

An Elegant Solution
Just to show how elegant a functional solution can be, let's walk through the process of building up our function. We'll break it down into the following parts:

1. Fibonacci sequence whose values do not exceed 4 million
2. Even-valued terms
3. Sum

We already have a function that gives us terms that do not exceed 21, so changing this to not exceed four million is pretty easy:

testFibs = [x | x <- takeWhile (<= 4000000) fibs]
  where
    fibs = 1 : 2 : zipWith (+) fibs (tail fibs)

And the result:

[1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368,75025,121393,196418,317811,514229,832040,1346269,2178309,3524578]

Now, we just need to add an additional filter to only pick out the even values. I say "additional filter" because the "takeWhile" function is a filter that limits the "fibs" sequence to a finite set of values. Lucky for us, Haskell has an "even" function built in.

testFibs = [x | x <- takeWhile (<= 4000000) fibs, even x]
  where
    fibs = 1 : 2 : zipWith (+) fibs (tail fibs)

And the result:

[2,8,34,144,610,2584,10946,46368,196418,832040,3524578]

This cuts the sequence down quite a bit.

Our last step is to simply sum up the values. Let's rename the function as well:

euler2 = sum [x | x <- takeWhile (<= 4000000) fibs, even x]
  where
    fibs = 1 : 2 : zipWith (+) fibs (tail fibs)

And this gives us the answer:

4613732

And we know this is correct by asking the internet for the solution to Euler Problem #2.

Wrap Up
There's a certain amount of elegance to this solution. We have built up a solution using several smaller functions, including "fibs", "takeWhile", "even", and "sum". Each function is responsible for doing a single thing. And we combine these small units to create more complex, useful functions.

So, as you can tell, I'm becoming a fan of functional programming. But we want to make sure that we're using the right tool for the job. Is functional programming the right solution for every problem? Absolutely not. But there are some problems (such as the one we looked at here) that really lend themselves to a functional solution. The same code written imperatively would be much more complex.

But we can take these functional solutions and apply them to a more general-purpose language like C#. At its heart, C# is an imperative language that has its roots in object-oriented programming. But it has also had many functional features added on over the years. We can start to "think functionally" and use those features more effectively. And if those features fall short, we also have the option of using F#, a .NET language built from the beginning as a functional language.

I always want to use the right tool for the job. And that means learning about as many tools as possible and figuring out where they fit in the toolbox.

Happy Coding!

Tuesday, January 28, 2014

A Functional Fibonacci Sequence (as Learned by an Imperative Programmer)

As mentioned previously, I've been interested in learning different programming languages and paradigms, not necessarily so that I can program in those languages, but so that I can improve my everyday programming in my main language.

One of my favorite things to noodle around with is the Fibonacci Sequence. This is just complicated enough to be interesting but not too complicated to be overwhelming. The sequence is easy enough:
1, 1, 2, 3, 5, 8, 13, 21, ...
Each item in the sequence is created by adding the 2 previous numbers. The sequence can start with either 0 or 1 (I use the version that starts with 1). So, the sequence goes like this:
 1
 1 = 1 + 0
 2 = 1 + 1
 3 = 1 + 2
 5 = 2 + 3
 8 = 3 + 5
13 = 5 + 8
21 = you get the idea
This is a classic example of a sequence that would lend itself to a recursive function. In my example code, I calculate the sequence using some global variables, but that's usually because the focus of the example is not on the Fibonacci sequence itself but on the code that uses it.

A Recursive Implementation
When I started diving into Haskell, I thought that implementing a Fibonacci sequence would be a good way for me to get familiar with the language. My first implementation included 2 functions.
fibonacci :: Int -> Int
fibonacci 1 = 1
fibonacci 2 = 1
fibonacci n = fibonacci(n-1) + fibonacci(n-2)
This method takes a parameter for the position of the number in the sequence. So, for example, if we call "fibonacci 4", it will give us the 3rd number in the Fibonacci sequence (which is 3). It does this with a recursive call. Let's walk through the implementation above.

"fibonacci 4" would use the last case ("fibonacci n") and would result in the following:
fibonacci (4-1) + fibonacci (4-2)
Reduced to
fibonacci 3 + fibonacci 2
"fibonacci 2" is equal to "1". Let's put that in:
fibonacci 3 + 1
"fibonacci 3" will use the "fibonacci n" case. Let's expand that:
fibonacci (3-1) + fibonacci (3-2) + 1
or
fibonacci 2 + fibonacci 1 + 1
And since "fibonacci 2" equals 1 and "fibonacci 1" also equals 1, we're left with
1 + 1 + 1
So, the 4th item in the Fibonacci sequence is 3.

To create a sequence of these numbers, I created another recursive function:
fibonacciList :: Int -> [Int]
fibonacciList 1 = [1]
fibonacciList n = fibonacciList (n-1) ++ [fibonacci n]
To use this method, we just pass in the number of items in our Fibonacci sequence, and it returns a list with the specified number of items.
fibonacciList 8
[1,1,2,3,5,8,13,21]
This function basically builds a list based on the "fibonacci" function. So, it creates the elements of the list by running "fibonacci 1", then "fibonacci 2", "fibonacci 3", ... "fibonacci n". We won't go through the recursion step-by-step.

A Big Problem
I was pretty proud of myself when I came up with these methods. I created a Fibonacci sequence, and I did it "functionally". But these methods have a big problem.

Since the "fibonacciList" function calls "fibonacci n" for each element, it actually ends up recalculating all of the previous numbers each time. What this means is that it works fine for small lists. But once we get to slightly larger lists, we find that the calculation slows down significantly. In fact, "fibnonacci 35" takes over 20 seconds to calculate on my laptop. That seems pretty ridiculous.

I was actually working on Euler problem #2 when I ran into this performance issue. And it really bothered me. So, I started to look for other solutions to calculate the Fibonacci sequence.

Note: I talked about Euler problems when I worked through Euler problem #1 on my blog.

A Truly Functional Fibonacci
I ran across this implementation that is very simple. But since my brain isn't quite used to thinking functionally, this completely escaped me. And when I saw the solution, it took a while before I really understood what it was doing.
fibs = 1 : 1 : zipWith (+) fibs (tail fibs)
The ":" operator basically creates list elements. So, when we have "1 : 1", this is the equivalent of the list "[1,1]". This function will actually create an infinite list of Fibonacci numbers (which is really interesting to run). We can wrap this in a list comprehension to return a list that stops once we get to 21.
testFibs = [x | x <- takeWhile (<= 21) fibs]
[1,1,2,3,5,8,13,21]
This works because Haskell is lazy evaluated. So even though the "fibs" list is infinite, Haskell will stop processing once it hits the takeWhile limitation that we set (<= 21).

Let's think about how "fibs" works. The "zipWith" function applies a function (in this case "+") to two lists. The lists are "fibs" (which is the current list) and "tail fibs" which includes everything but the first item. Again, this takes a bit to wrap your head around since these are recursive calls that are lazy evaluated.

It ends up creating a sort of ongoing offset list.
               [1, 1]
               [1]
         [1, 1, 2]
Here "fibs" is [1,1] and "tail fibs" is [1]. When we add these together (ignoring any "leftover" elements), we get [2] which is appended to the list.
            [1, 1, 2]
            [1, 2]
      [1, 1, 2, 3]
To get the 4th item, "fibs" is [1,1,2] and "tail fibs" is [1,2]. When we add these, we get [2, 3].
         [1, 1, 2, 3]
         [1, 2, 3]
   [1, 1, 2, 3, 5]
This continues, we can see that adding the columns from the diagram, fibs + tail fibs = the rest of the Fibonacci sequence.
      [1, 1, 2, 3, 5]
      [1, 2, 3, 5]
[1, 1, 2, 3, 5, 8]
Okay, so I don't know if the diagrams make it more clear or less clear. The combination of lazy evaluation with the recursiveness makes this quite a brain twister. But once it "clicks", it's extremely cool.

Wrap Up
So, what I've learned from this exercise is that I'll have to do a lot more work with functional programming for this to become more natural. I know that my first pass won't always be correct. But, I've found that solving a complex problem by building the solution up from smaller steps is a pretty amazing way to work.

I'll keep exploring functional programming (heading into F# soon), and I'm looking forward to making my brain think in new ways.

Happy Coding!