Wednesday, 7 November 2007

TDD and F#

I've been playing around with F# a bit more lately since its been announed that its going to be a first class language in .NET.

The following code is some TDD'd code that does classic Bowling game TDD example. Interestingly it came out to be 10 lines of code, which is much shorter than equivilent C# / C++ / Java implementations. However I'm not entirely sure its super readable. It was super writable. It was very easy to add in new cases as the code progressed. I'm also not sure how much more I could improve this as I'm not a F# whizz. This peice of code makes use of F#s pattern matching abilities (not regex pattern patching). However there are so many other toys in the language to play with. I think async will be the next thing I have a play with.

The F# community is pretty small at the moment, it will be interesting to see how people exploit the language as it becomes more main stream. I also posted this code over at the hubFS .



#light

open System

open NUnit.Framework

let rec score_bowls bowls =

let rec score_bowls' frame l =

match l with

| _ when frame = 10 -> (List.fold_right (fun x y -> x + y) l 0)

| [] -> 0

| [f] -> f

| [f;s] -> f + s

| f :: s :: n :: tail when f = 10 -> 10 + s + n + score_bowls' (frame+1) ( s :: n :: tail )

| f :: s :: n :: tail when (f + s) = 10 -> 10 + n + score_bowls' (frame+1) (n :: tail)

| f :: s :: n :: tail -> f + s + score_bowls' (frame+1) (n :: tail)

score_bowls' 1 bowls

[]

type BowlingTestCases = class

new() = {}

[]

member x.SimpleScoring() = Assert.AreEqual(6, score_bowls [1;2;3] )

[]

member x.ScoreSpare() = Assert.AreEqual(12, score_bowls [2;8;1] )

[]

member x.ScoreStrike() = Assert.AreEqual(16, score_bowls [10;1;2] )

[]

member x.ScorePerfectGame() = Assert.AreEqual( 300, score_bowls [for i in 1..12 -> 10] )

[]

member x.SpareLastFrame() = Assert.AreEqual( 11, score_bowls ([for i in 1..18 -> 0] @ [2;8;1]) )

[]

member x.StrikeLastFrame() = Assert.AreEqual( 21, score_bowls ([for i in 1..18 -> 0] @ [10;10;1]) )

end

3 comments:

Harris said...

F# is quite cool.. I always like recursions =)

Just curious though, the code produces 32 for [10;10;1] and it'd be better if it was either 21 (exclude the incomplete scores) or 33 (includes everything).
The later can be done in only one additional matching which is quite cool: [10;s] -> 10 + s + s

And in fact the other three base cases can be replaced with
_ -> (List.fold_right (fun x y -> x + y) l 0)
at the end.

Keith said...

good catch.

I added in the case.

I quite like how easy it is to handle more cases through pattern matching.

My next step is trying to reduce some of the duplication in it.... however I'm not sure if that helps or hinders the code....

Randy MacDonald said...

I've seen this done in 3 J sentences, 3 instead of 1 for clarity reasons. It was clear enough for Ron Jeffries to turn it into 50 lines of Java.