I’ve spent some of today playing around with F# and here are the results:
I am working on a little app that takes a list of numbers does various things with them and then plots the results on a chart using the Google Charts API.
To do this I need to take an F# list of ints and convert it to a comma delimited string.
Now, if anyone knows of a library function that does this, then great, thanks, let me know and I’ll use it next time, but this time it seemed like a good opportunity to try to think about algorithms in a more functional way.
So, if I was doing this in C# I’d write something like:
string s = "";
foreach(int i in list)
{
s += i + ",";
}
return s.TrimEnd(',');
But entering into the spirit of functional programming I want to try to avoid loops and mutable state so came up with this:
let rec listAsString (l : int list) =
match l with
|[] -> ""
|head::tail ->
if List.length tail = 1 then string tail.[0]
else string head + "," + listAsString tail
No mutable state, uses recursion rather than loops, great, I’m feeling pretty smug. But… that if statement looks a bit ugly, so I thought I’d see if I could find a more elegant solution.
So then I dug out a blog post by Chris Smith on active patterns and that seemed like a good solution.
So I wrote an active pattern to match the 3 states of the list that I’m interested in (empty, one element left, more than one character left)
let (|MoreThanOne|LastElement|EmptyList|)(l : int list) =
if List.length l = 0 then
EmptyList
elif List.length l = 1 then
LastElement(List.hd l)
else
let head::tail = l
MoreThanOne(head, tail)
And this lets me amend my function as follows:
let rec listAsString (l : int list) =
match l with
|EmptyList -> ""
|MoreThanOne(head, tail) -> string head + "," + listAsString tail
|LastElement x -> string x
Which seems both concise and readable.
Then I realised that actually I could do the same thing with a lot less code as follows:
let listAsString(l: int list) =
List.fold_left (fun acc i -> acc + string i + ",") "" l
|> fun s -> s.TrimEnd [|','|]
That is one of the things I’m enjoying about F#, there always seem to be a number of different approaches to any problem.