Saturday, December 10, 2011

Porting Bryan's Erlang Function to F#

A week or two ago, Fresh Brewed Coder Bryan Hunter posted a video to explain how to debug Erlang apps. In the tutorial, he stepped through a recursive function to display the various concepts. I thought it would be interesting to take that simple example and explore a few ways to write it in F#. Let's start by reviewing Bryan's example (which calculates the average of a provided list of number).

Here's what his average.erl source file looks like:
-module(average).

-export([calculate/1]).

calculate(X) ->
     calculate(X,0,0).

calculate([H|T], Length, Sum) ->
     calculate(T, Length+1, Sum+H);

calculate([], Length, Sum) ->
     Sum/Length.
Porting the Code to F#:

So how would you write this in F#? As with any language, there are several options. A fairly straight forward port might look like this:
let calculate x =
    let rec calc list length sum =
        match list with
        | head :: tail -> calc tail (length+1) (sum+head)
        | [] -> sum/length
    calc x 0 0
printfn "Value is %O" (calculate [10;22])
Breaking it Down:

The above code defines the calculate function that takes a list of integers as a single argument.

Next, a recursive function (the "rec" keyword indicates that it is recursive) named calc is defined (line 2). This function takes a list of integers as the first argument, the current length as the second argument, and the current sum as the last argument.

At the heart of this recursive function is a pattern match against the provided list (line 3). Using something known as the cons pattern, the first pattern (line 4) attempts to decompose the provided list into a "head" (the first element in the list) and "tail" (the rest of the elements).

If the first pattern is a match, the calc function is called with the "tail" list as the first argument, the length value incremented by 1 as the second argument, and the result of the sum value combined with the first value from the list (i.e. the head) as the third argument. This continues until the list is empty, at which point the cons pattern no longer results in a match and the second pattern is evaluated.

By the time the second pattern (line 5) is evaluated, the match will succeed due to the list now being empty. This causes the average to be calculated (i.e. sum/length) and returned.

Line 6 kicks off the initial call to the calc recursive function with the initial list as the first argument and default length and sum values of 0.

Finally, the last line (line 7) kicks off the whole thing with the call to calculate the average of the numbers 10 and 22 (as used in Bryan's demo).

Another Approach:

While  the approach above is pretty compact, F# provides a library that contains a high-order function that makes this even easier. The following line of code will also calculate the average of the values in a list of integers:
[10;22] |> List.averageBy (fun number -> float number)

3 comments:

  1. It may help the reader to understand that in your last example float is a function (as opposed to a C# cast) and as such it's possible to call the averageBy function like so:

    [10;22] |> List.averageBy float

    ReplyDelete
  2. Thanks, that's a good point!

    ReplyDelete