Keith,

That's a pretty cool program. Your representation is pretty concise. I've posted a different version below that is tail recursive (could be construed as pointless, as it is capped at 10 frames by the second match case below).

I only did some cursory testing, but the logic seems sound.

1
2
3
4
5
6
7
8
9
10
11
 
let score_bowls bowls =
  let rec sb frame l score =
    match l with
    | [] -> score
    | _ when frame = 10 -> score
    | [f] -> sb (frame+1) [] (score + f)
    | 10 :: s :: n :: tail -> sb (frame+1) (s::n::tail) (score+10+s+n)
    | f :: s :: n :: tail when (f + s) = 10 -> sb (frame+1) (n::tail) (score+10+n)
    | f :: s :: tail -> sb (frame+1) tail (score+f+s)
  sb 0 bowls 0

In this code, I am using frame to represent how many frames have been processed so far. score is the accumulator, while l represents the remaining frames to process. The main difference, other than the accumulator, is that I have removed the match case [f; s], which is handled by the last case above.

The equivalent non-tail recursive function is:

1
2
3
4
5
6
7
8
9
10
let score_bowls2 bowls =
  let rec sb frame l =
    match l with
    | [] -> 0
    | _ when frame = 10 -> 0
    | [f] -> f
    | 10 :: s :: n :: tail -> 10 + s + n + sb (frame+1) (s::n::tail)
    | f :: s :: n :: tail when (f + s) = 10 -> 10 + n + sb (frame+1) (n::tail)
    | f :: s :: tail -> f + s + sb (frame+1) tail
  sb 0 bowls

Regards,

z.

By on 11/7/2007 2:00 AM ()

Continuing to try and improve my initial code and remove some of the duplication (and fix a bug)

<!--

{\rtf1\ansi\ansicpg\lang1024\noproof1252\uc1 \deff0{\fonttbl{\f0\fnil\fcharset0\fprq1 Courier New;}}{\colortbl;??\red0\green0\blue0;\red255\green255\blue255;\red0\green0\blue255;}??\fs28 #light\par ??\par ??\cf3 open\cf0 System\par ??\cf3 open\cf0 NUnit.Framework\par ??\par ??\cf3 let\cf0 \cf3 rec\cf0 score_bowls bowls =\par ?? \cf3 let\cf0 \cf3 rec\cf0 score_bowls' frame l = \par ?? \cf3 let\cf0 sum b = (List.fold_right (\cf3 fun\cf0 x y \cf3 ->\cf0 x + y) b 0)\par ?? \cf3 let\cf0 nextframe = score_bowls' (frame+1)\par ?? \cf3 match\cf0 l \cf3 with\cf0 \par ?? | _ \cf3 when\cf0 frame = 10 \cf3 ->\cf0 sum l \par ?? | [10;s] \cf3 ->\cf0 10 + s + s\par ?? | 10 :: s :: n :: tail \cf3 ->\cf0 10 + s + n + nextframe (s :: n :: tail ) \par ?? | f :: s :: n :: tail \cf3 ->\cf0 f + s + (\cf3 if\cf0 ((f+s)=10) \cf3 then\cf0 n \cf3 else\cf0 0) + nextframe (n :: tail)\par ?? | _ \cf3 ->\cf0 sum l \par ?? score_bowls' 1 bowls\par ?? \par ??[<TestFixture>]\par ??\cf3 type\cf0 BowlingTestCases = \cf3 class\par ??\cf0 \cf3 new\cf0 () = \{\} \par ?? [<Test>]\par ?? \cf3 member\cf0 x.SimpleScoring() = Assert.AreEqual(6, score_bowls [1;2;3] )\par ?? [<Test>]\par ?? \cf3 member\cf0 x.ScoreSpare() = Assert.AreEqual(12, score_bowls [2;8;1] )\par ?? [<Test>]\par ?? \cf3 member\cf0 x.ScoreStrike() = Assert.AreEqual(16, score_bowls [10;1;2] )\par ?? [<Test>] \par ?? \cf3 member\cf0 x.ScorePerfectGame() = Assert.AreEqual( 300, score_bowls [\cf3 for\cf0 i \cf3 in\cf0 1..12 \cf3 ->\cf0 10] )\par ?? [<Test>] \par ?? \cf3 member\cf0 x.SpareLastFrame() = Assert.AreEqual( 11, score_bowls ([\cf3 for\cf0 i \cf3 in\cf0 1..18 \cf3 ->\cf0 0] @ [2;8;1]) )\par ?? [<Test>] \par ?? \cf3 member\cf0 x.StrikeLastFrame() = Assert.AreEqual( 21, score_bowls ([\cf3 for\cf0 i \cf3 in\cf0 1..18 \cf3 ->\cf0 0] @ [10;10;1]) )\par ?? [<Test>] \par ?? \cf3 member\cf0 x.DoubleStrike() = Assert.AreEqual(33, score_bowls [10;10;1] )\par ??\cf3 end\par ??\par ??}

-->

#light

open System

open NUnit.Framework

let rec score_bowls bowls =

let rec score_bowls' frame l =

let sum b = (List.fold_right (fun x y -> x + y) b 0)

let nextframe = score_bowls' (frame+1)

match l with

| _ when frame = 10 -> sum l

| [10;s] -> 10 + s + s

| 10 :: s :: n :: tail -> 10 + s + n + nextframe (s :: n :: tail )

| f :: s :: n :: tail -> f + s + (if((f+s)=10) then n else 0) + nextframe (n :: tail)

| _ -> sum l

score_bowls' 1 bowls

[<TestFixture>]

type BowlingTestCases = class

new() = {}

[<Test>]

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

[<Test>]

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

[<Test>]

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

[<Test>]

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

[<Test>]

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

[<Test>]

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

[<Test>]

member x.DoubleStrike() = Assert.AreEqual(33, score_bowls [10;10;1] )

end

By on 11/8/2007 8:12 PM ()

another shot... cleaning things up a bit<!--

{\rtf1\ansi\ansicpg\lang1024\noproof1252\uc1 \deff0{\fonttbl{\f0\fnil\fcharset0\fprq1 Courier New;}}{\colortbl;??\red0\green0\blue0;\red255\green255\blue255;\red0\green0\blue255;}??\fs28 #light\par ??\par ??\cf3 open\cf0 System\par ??\cf3 open\cf0 NUnit.Framework\par ??\par ??\cf3 let\cf0 score_bowls bowls =\par ?? \cf3 let\cf0 \cf3 rec\cf0 score_bowls' frame l = \par ?? \cf3 let\cf0 nextframe = score_bowls' (frame+1)\par ?? \cf3 match\cf0 l \cf3 with\cf0 \par ?? | _ \cf3 when\cf0 frame = 11 \cf3 ->\cf0 0 \par ?? | [10;s] \cf3 ->\cf0 10 + s + s\par ?? | 10 :: s :: n :: tail \cf3 ->\cf0 10 + s + n + nextframe (s :: n :: tail ) \par ?? | f :: s :: n :: tail \cf3 ->\cf0 f + s + (\cf3 if\cf0 ((f+s)=10) \cf3 then\cf0 n \cf3 else\cf0 0) + nextframe (n :: tail)\par ?? | _ \cf3 ->\cf0 List.fold_right (\cf3 fun\cf0 x y \cf3 ->\cf0 x + y) l 0\par ?? score_bowls' 1 bowls\par ?? \par ??[<TestFixture>]\par ??\cf3 type\cf0 BowlingTestCases = \cf3 class\par ??\cf0 \cf3 new\cf0 () = \{\} \par ?? [<Test>]\par ?? \cf3 member\cf0 x.SimpleScoring() = Assert.AreEqual(6, score_bowls [1;2;3] )\par ?? [<Test>]\par ?? \cf3 member\cf0 x.ScoreSpare() = Assert.AreEqual(12, score_bowls [2;8;1] )\par ?? [<Test>]\par ?? \cf3 member\cf0 x.ScoreStrike() = Assert.AreEqual(16, score_bowls [10;1;2] )\par ?? [<Test>] \par ?? \cf3 member\cf0 x.ScorePerfectGame() = Assert.AreEqual( 300, score_bowls [\cf3 for\cf0 i \cf3 in\cf0 1..12 \cf3 ->\cf0 10] )\par ?? [<Test>] \par ?? \cf3 member\cf0 x.SpareLastFrame() = Assert.AreEqual( 11, score_bowls ([\cf3 for\cf0 i \cf3 in\cf0 1..18 \cf3 ->\cf0 0] @ [2;8;1]) )\par ?? [<Test>] \par ?? \cf3 member\cf0 x.StrikeLastFrame() = Assert.AreEqual( 21, score_bowls ([\cf3 for\cf0 i \cf3 in\cf0 1..18 \cf3 ->\cf0 0] @ [10;10;1]) )\par ?? [<Test>] \par ?? \cf3 member\cf0 x.DoubleStrike() = Assert.AreEqual(33, score_bowls [10;10;1] )\par ??\cf3 end\par ??\par ??}

-->

#light

open System

open NUnit.Framework

let score_bowls bowls =

let rec score_bowls' frame l =

let nextframe = score_bowls' (frame+1)

match l with

| _ when frame = 11 -> 0

| [10;s] -> 10 + s + s

| 10 :: s :: n :: tail -> 10 + s + n + nextframe (s :: n :: tail )

| f :: s :: n :: tail -> f + s + (if((f+s)=10) then n else 0) + nextframe (n :: tail)

| _ -> List.fold_right (fun x y -> x + y) l 0

score_bowls' 1 bowls

[<TestFixture>]

type BowlingTestCases = class

new() = {}

[<Test>]

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

[<Test>]

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

[<Test>]

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

[<Test>]

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

[<Test>]

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

[<Test>]

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

[<Test>]

member x.DoubleStrike() = Assert.AreEqual(33, score_bowls [10;10;1] )

end

By on 11/8/2007 8:29 PM ()

Just if anyones interested...

a friend tipped me off to another thing... (fun x y -> x + y) is the + operator..... obviously enough in retrospect.

Still not happy with the embedded if statement though.... I don't think its too clear.

my match basically is

if we are at the end of the game, dont score anymore frames

if we have an incomplete strike, score what we can

if strike, score strike and carry on

if normal frame, score it, if we got a spare then add on the extra and then carry on

otherwise, just add up whats there.

Thing is, without comments I can't see a way of introducing the language too easily. In C# / C++ you'd introudce an explaining variable / methods. Here I kind need a new kind of refactoring called "Introduce explaining pattern match" but I don't think I can do that. I'm not sure if it would help understanding if I could. It's still way more concise than any C# / Java / C++ I've seen for the same thing.

so...the code<!--

{\rtf1\ansi\ansicpg\lang1024\noproof1252\uc1 \deff0{\fonttbl{\f0\fnil\fcharset0\fprq1 Courier New;}}{\colortbl;??\red0\green0\blue0;\red255\green255\blue255;\red0\green0\blue255;}??\fs28 #light\par ??\par ??\cf3 open\cf0 System\par ??\cf3 open\cf0 NUnit.Framework\par ??\par ??\cf3 let\cf0 score_bowls bowls =\par ?? \cf3 let\cf0 \cf3 rec\cf0 score_bowls' frame l = \par ?? \cf3 let\cf0 nextframe = score_bowls' (frame+1)\par ?? \cf3 match\cf0 l \cf3 with\cf0 \par ?? | _ \cf3 when\cf0 frame = 11 \cf3 ->\cf0 0 \par ?? | [10;s] \cf3 ->\cf0 10 + s + s\par ?? | 10 :: s :: n :: tail \cf3 ->\cf0 10 + s + n + nextframe (s :: n :: tail ) \par ?? | f :: s :: n :: tail \cf3 ->\cf0 f + s + (\cf3 if\cf0 ((f+s)=10) \cf3 then\cf0 n \cf3 else\cf0 0) + nextframe (n :: tail)\par ?? | _ \cf3 ->\cf0 List.fold_right (+) l 0\par ?? score_bowls' 1 bowls\par ?? \par ??[<TestFixture>]\par ??\cf3 type\cf0 BowlingTestCases = \cf3 class\par ??\cf0 \cf3 new\cf0 () = \{\} \par ?? [<Test>]\par ?? \cf3 member\cf0 x.SimpleScoring() = Assert.AreEqual(6, score_bowls [1;2;3] )\par ?? [<Test>]\par ?? \cf3 member\cf0 x.ScoreSpare() = Assert.AreEqual(12, score_bowls [2;8;1] )\par ?? [<Test>]\par ?? \cf3 member\cf0 x.ScoreStrike() = Assert.AreEqual(16, score_bowls [10;1;2] )\par ?? [<Test>] \par ?? \cf3 member\cf0 x.ScorePerfectGame() = Assert.AreEqual( 300, score_bowls [\cf3 for\cf0 i \cf3 in\cf0 1..12 \cf3 ->\cf0 10] )\par ?? [<Test>] \par ?? \cf3 member\cf0 x.SpareLastFrame() = Assert.AreEqual( 11, score_bowls ([\cf3 for\cf0 i \cf3 in\cf0 1..18 \cf3 ->\cf0 0] @ [2;8;1]) )\par ?? [<Test>] \par ?? \cf3 member\cf0 x.StrikeLastFrame() = Assert.AreEqual( 21, score_bowls ([\cf3 for\cf0 i \cf3 in\cf0 1..18 \cf3 ->\cf0 0] @ [10;10;1]) )\par ?? [<Test>] \par ?? \cf3 member\cf0 x.DoubleStrike() = Assert.AreEqual(33, score_bowls [10;10;1] )\par ?? [<Test>] \par ?? \cf3 member\cf0 x.ExampleGame() = Assert.AreEqual(133, score_bowls [1;4;4;5;6;4;5;5;10;0;1;7;3;6;4;10;2;8;6] )\par ??\cf3 end\par ??\par ??}

-->

#light

open System

open NUnit.Framework

let score_bowls bowls =

let rec score_bowls' frame l =

let nextframe = score_bowls' (frame+1)

match l with

| _ when frame = 11 -> 0

| [10;s] -> 10 + s + s

| 10 :: s :: n :: tail -> 10 + s + n + nextframe (s :: n :: tail )

| f :: s :: n :: tail -> f + s + (if((f+s)=10) then n else 0) + nextframe (n :: tail)

| _ -> List.fold_right (+) l 0

score_bowls' 1 bowls

[<TestFixture>]

type BowlingTestCases = class

new() = {}

[<Test>]

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

[<Test>]

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

[<Test>]

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

[<Test>]

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

[<Test>]

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

[<Test>]

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

[<Test>]

member x.DoubleStrike() = Assert.AreEqual(33, score_bowls [10;10;1] )

[<Test>]

member x.ExampleGame() = Assert.AreEqual(133, score_bowls [1;4;4;5;6;4;5;5;10;0;1;7;3;6;4;10;2;8;6] )

end

By on 11/9/2007 11:02 PM ()

Thanks a bunch for the examples! I do a lot of test-driven development and I have a project called FsUnit that I could use some input on.

My project has been up on Google Code for a while now: [link:code.google.com]

Please check it out if you're interested and let me know what I can add or change. Right now it's just a syntactic wrapper for NUnit but I would like to take this project much further.

Using FsUnit, you can write assertions like this:

1
2
3
4
5
6
7
8
 

score_bowls [1;2;3] |> should (equal 6)

[|"item1"|] |> should (contain "item1")

true |> should (be True)
By on 11/13/2007 8:01 AM ()

If you want to better express the intent of the code then I would probably go with something like I've done below, however, it's just under twice as long as your version so it really comes down to a trade of. Whats more important code size or readability? In this case I'd probably go with code size and just add some comments since the function itself isn't doing anything overly complicated.

#light

let (|EndOfGame|IncompleteStrike|Strike|Normal|Other|) (l, frame) =
match l with
_ when frame = 11 -> EndOfGame(0)
| [10;s] -> IncompleteStrike(10 + s + s)
| 10 :: s :: n :: tail -> Strike(10 + s + n, s :: n :: tail)
| f :: s :: n :: tail when f + s = 10 -> Normal(f + s + n, n :: tail)
| f :: s :: n :: tail -> Normal(f + s , n :: tail)
| ls -> Other(List.fold_left (+) 0 ls)

let score_bowls bowls =
let rec score_bowls' frame l current_score =
let nextframe = score_bowls' (frame+1)
match (l, frame) with
EndOfGame(score) -> current_score + score
| IncompleteStrike(score) -> current_score + score
| Strike(score, l) -> nextframe l (current_score + score)
| Normal(score, l) -> nextframe l (current_score + score)
| Other(score) -> current_score + score
score_bowls' 1 bowls

By on 11/11/2007 9:02 PM ()
IntelliFactory Offices Copyright (c) 2011-2012 IntelliFactory. All rights reserved.
Home | Products | Consulting | Trainings | Blogs | Jobs | Contact Us | Terms of Use | Privacy Policy | Cookie Policy
Built with WebSharper