trackler 2.0.5.9 → 2.0.5.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/common/TOPICS.txt +1 -0
- data/lib/trackler/version.rb +1 -1
- data/tracks/csharp/config.json +9 -0
- data/tracks/csharp/exercises/go-counting/Example.cs +147 -0
- data/tracks/csharp/exercises/go-counting/GoCountingTest.cs +127 -0
- data/tracks/fsharp/config.json +15 -7
- data/tracks/fsharp/docs/LEARNING.md +15 -5
- data/tracks/fsharp/docs/RESOURCES.md +15 -3
- data/tracks/fsharp/exercises/bank-account/BankAccountTest.fs +39 -11
- data/tracks/fsharp/exercises/bank-account/Example.fs +22 -17
- data/tracks/fsharp/exercises/book-store/BookStoreTest.fs +64 -0
- data/tracks/fsharp/exercises/book-store/Example.fs +52 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3a4a09d238b30d68369e40d493a03b8f095a1803
|
4
|
+
data.tar.gz: f5e9f063daeaf6f628c06eaa82147898c68ef380
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5428931a7a5b48c186986179706882feeb43c2f422e20d177bd132256888b0caa72ec7a8a22679ba178b85dd629a8487889856875bcdd5dd1dceb307864c9618
|
7
|
+
data.tar.gz: a63b460b8a05caff7fd55f4e4310b933188426ef3e685dbbd08cbae30c58bdaa12d1b8e0eb69b548c93623c4edce95c3bd88575394ffe3d7a891f4cd640367b8
|
data/common/TOPICS.txt
CHANGED
data/lib/trackler/version.rb
CHANGED
data/tracks/csharp/config.json
CHANGED
@@ -0,0 +1,147 @@
|
|
1
|
+
using System;
|
2
|
+
using System.Drawing;
|
3
|
+
using System.Collections.Generic;
|
4
|
+
using System.Linq;
|
5
|
+
|
6
|
+
public class GoCounting
|
7
|
+
{
|
8
|
+
public enum Player
|
9
|
+
{
|
10
|
+
None,
|
11
|
+
Black,
|
12
|
+
White
|
13
|
+
}
|
14
|
+
|
15
|
+
private readonly Player[][] board;
|
16
|
+
|
17
|
+
public GoCounting(string input)
|
18
|
+
{
|
19
|
+
board = ParseBoard(input);
|
20
|
+
}
|
21
|
+
|
22
|
+
private static Player CharToPlayer(char c)
|
23
|
+
{
|
24
|
+
switch (c)
|
25
|
+
{
|
26
|
+
case 'B': return Player.Black;
|
27
|
+
case 'W': return Player.White;
|
28
|
+
default: return Player.None;
|
29
|
+
}
|
30
|
+
}
|
31
|
+
|
32
|
+
private static Player[][] ParseBoard(string input)
|
33
|
+
{
|
34
|
+
var split = input.Split('\n');
|
35
|
+
var rows = split.Length;
|
36
|
+
var cols = split[0].Length;
|
37
|
+
|
38
|
+
return split.Select(row => row.Select(CharToPlayer).ToArray()).ToArray();
|
39
|
+
}
|
40
|
+
|
41
|
+
private int Cols => board[0].Length;
|
42
|
+
private int Rows => board.Length;
|
43
|
+
|
44
|
+
private bool IsValidCoordinate(Point coordinate) =>
|
45
|
+
coordinate.Y >= 0 && coordinate.Y < Rows &&
|
46
|
+
coordinate.X >= 0 && coordinate.X < Cols;
|
47
|
+
|
48
|
+
private Player GetPlayer(Point coordinate) => board[coordinate.Y][coordinate.X];
|
49
|
+
|
50
|
+
private bool IsEmpty(Point coordinate) => GetPlayer(coordinate) == Player.None;
|
51
|
+
private bool IsTaken(Point coordinate) => !IsEmpty(coordinate);
|
52
|
+
|
53
|
+
private IEnumerable<Point> EmptyCoordinates()
|
54
|
+
{
|
55
|
+
return Enumerable.Range(0, Cols).SelectMany(col =>
|
56
|
+
Enumerable.Range(0, Rows).Select(row => new Point(col, row)))
|
57
|
+
.Where(IsEmpty);
|
58
|
+
}
|
59
|
+
|
60
|
+
private IEnumerable<Point> NeighborCoordinates(Point coordinate)
|
61
|
+
{
|
62
|
+
var row = coordinate.Y;
|
63
|
+
var col = coordinate.X;
|
64
|
+
|
65
|
+
var coords = new[]
|
66
|
+
{
|
67
|
+
new Point(col, row - 1),
|
68
|
+
new Point(col-1, row),
|
69
|
+
new Point(col+1, row),
|
70
|
+
new Point(col, row+1)
|
71
|
+
};
|
72
|
+
|
73
|
+
return coords.Where(IsValidCoordinate);
|
74
|
+
}
|
75
|
+
|
76
|
+
private IEnumerable<Point> TakenNeighborCoordinates(Point coordinate) =>
|
77
|
+
NeighborCoordinates(coordinate).Where(IsTaken);
|
78
|
+
|
79
|
+
private IEnumerable<Point> EmptyNeighborCoordinates(Point coordinate) =>
|
80
|
+
NeighborCoordinates(coordinate).Where(IsEmpty);
|
81
|
+
|
82
|
+
private Player TerritoryOwner(HashSet<Point> coords)
|
83
|
+
{
|
84
|
+
var neighborColors = coords.SelectMany(TakenNeighborCoordinates).Select(GetPlayer);
|
85
|
+
var uniqueNeighborColors = ToSet(neighborColors);
|
86
|
+
|
87
|
+
if (uniqueNeighborColors.Count == 1)
|
88
|
+
return uniqueNeighborColors.First();
|
89
|
+
|
90
|
+
return Player.None;
|
91
|
+
}
|
92
|
+
|
93
|
+
private HashSet<Point> TerritoryHelper(HashSet<Point> remainder, HashSet<Point> acc)
|
94
|
+
{
|
95
|
+
if (!remainder.Any())
|
96
|
+
return acc;
|
97
|
+
|
98
|
+
var emptyNeighbors = new HashSet<Point>(remainder.SelectMany(EmptyNeighborCoordinates));
|
99
|
+
emptyNeighbors.ExceptWith(acc);
|
100
|
+
|
101
|
+
var newAcc = ToSet(acc);
|
102
|
+
newAcc.UnionWith(emptyNeighbors);
|
103
|
+
return TerritoryHelper(emptyNeighbors, newAcc);
|
104
|
+
}
|
105
|
+
|
106
|
+
private HashSet<Point> Territory(Point coordinate) =>
|
107
|
+
IsValidCoordinate(coordinate) && IsEmpty(coordinate)
|
108
|
+
? TerritoryHelper(ToSingletonSet(coordinate), ToSingletonSet(coordinate))
|
109
|
+
: new HashSet<Point>();
|
110
|
+
|
111
|
+
public Tuple<Player, IEnumerable<Point>> TerritoryFor(Point coord)
|
112
|
+
{
|
113
|
+
var coords = Territory(coord);
|
114
|
+
if (!coords.Any())
|
115
|
+
return null;
|
116
|
+
|
117
|
+
var owner = TerritoryOwner(coords);
|
118
|
+
return Tuple.Create(owner, coords.AsEnumerable());
|
119
|
+
}
|
120
|
+
|
121
|
+
private Dictionary<Player, IEnumerable<Point>> TerritoriesHelper(HashSet<Point> remainder, Dictionary<Player, IEnumerable<Point>> acc)
|
122
|
+
{
|
123
|
+
if (!remainder.Any())
|
124
|
+
return acc;
|
125
|
+
|
126
|
+
var coord = remainder.First();
|
127
|
+
var coords = Territory(coord);
|
128
|
+
var owner = TerritoryOwner(coords);
|
129
|
+
|
130
|
+
var newRemainder = ToSet(remainder);
|
131
|
+
newRemainder.ExceptWith(coords);
|
132
|
+
|
133
|
+
var newAcc = new Dictionary<Player, IEnumerable<Point>>(acc);
|
134
|
+
newAcc[owner] = coords;
|
135
|
+
|
136
|
+
return TerritoriesHelper(newRemainder, newAcc);
|
137
|
+
}
|
138
|
+
|
139
|
+
public Dictionary<Player, IEnumerable<Point>> Territories()
|
140
|
+
{
|
141
|
+
var emptyCoords = EmptyCoordinates();
|
142
|
+
return TerritoriesHelper(ToSet(emptyCoords), new Dictionary<Player, IEnumerable<Point>>());
|
143
|
+
}
|
144
|
+
|
145
|
+
private static HashSet<T> ToSet<T>(IEnumerable<T> value) => new HashSet<T>(value);
|
146
|
+
private static HashSet<T> ToSingletonSet<T>(T value) => new HashSet<T> { value };
|
147
|
+
}
|
@@ -0,0 +1,127 @@
|
|
1
|
+
using NUnit.Framework;
|
2
|
+
using System;
|
3
|
+
using System.Collections.Generic;
|
4
|
+
using System.Drawing;
|
5
|
+
|
6
|
+
public class GoCountingTest
|
7
|
+
{
|
8
|
+
private static readonly string boardFiveByFive =
|
9
|
+
string.Join("\n", new[]
|
10
|
+
{
|
11
|
+
" B ",
|
12
|
+
" B B ",
|
13
|
+
"B W B",
|
14
|
+
" W W ",
|
15
|
+
" W "
|
16
|
+
});
|
17
|
+
|
18
|
+
private static readonly string board9x9 =
|
19
|
+
string.Join("\n", new[]
|
20
|
+
{
|
21
|
+
" B B ",
|
22
|
+
"B B B",
|
23
|
+
"WBBBWBBBW",
|
24
|
+
"W W W W W",
|
25
|
+
" ",
|
26
|
+
" W W W W ",
|
27
|
+
"B B B B",
|
28
|
+
" W BBB W ",
|
29
|
+
" B B "
|
30
|
+
});
|
31
|
+
|
32
|
+
[Test]
|
33
|
+
public void FiveByFiveTerritoryForBlack()
|
34
|
+
{
|
35
|
+
var board = new GoCounting(boardFiveByFive);
|
36
|
+
var result = board.TerritoryFor(new Point(0, 1));
|
37
|
+
Assert.That(result.Item1, Is.EqualTo(GoCounting.Player.Black));
|
38
|
+
Assert.That(result.Item2, Is.EquivalentTo(new[] { new Point(0, 0), new Point(0, 1), new Point(1, 0) }));
|
39
|
+
}
|
40
|
+
|
41
|
+
[Ignore("Remove to run test")]
|
42
|
+
[Test]
|
43
|
+
public void FiveByFiveTerritoryForWhite()
|
44
|
+
{
|
45
|
+
var board = new GoCounting(boardFiveByFive);
|
46
|
+
var result = board.TerritoryFor(new Point(2, 3));
|
47
|
+
Assert.That(result.Item1, Is.EqualTo(GoCounting.Player.White));
|
48
|
+
Assert.That(result.Item2, Is.EquivalentTo(new[] { new Point(2, 3) }));
|
49
|
+
}
|
50
|
+
|
51
|
+
[Ignore("Remove to run test")]
|
52
|
+
[Test]
|
53
|
+
public void FiveByFiveOpenTerritory()
|
54
|
+
{
|
55
|
+
var board = new GoCounting(boardFiveByFive);
|
56
|
+
var result = board.TerritoryFor(new Point(1, 4));
|
57
|
+
Assert.That(result.Item1, Is.EqualTo(GoCounting.Player.None));
|
58
|
+
Assert.That(result.Item2, Is.EquivalentTo(new[] { new Point(0, 3), new Point(0, 4), new Point(1, 4) }));
|
59
|
+
}
|
60
|
+
|
61
|
+
[Ignore("Remove to run test")]
|
62
|
+
[Test]
|
63
|
+
public void FiveByFiveNonTerritoryStone()
|
64
|
+
{
|
65
|
+
var board = new GoCounting(boardFiveByFive);
|
66
|
+
Assert.That(board.TerritoryFor(new Point(1, 1)), Is.Null);
|
67
|
+
}
|
68
|
+
|
69
|
+
[Ignore("Remove to run test")]
|
70
|
+
[Test]
|
71
|
+
public void FiveByFiveNonTerritoryDueToTooLowCoordinate()
|
72
|
+
{
|
73
|
+
var board = new GoCounting(boardFiveByFive);
|
74
|
+
Assert.That(board.TerritoryFor(new Point(-1, 1)), Is.Null);
|
75
|
+
}
|
76
|
+
|
77
|
+
[Ignore("Remove to run test")]
|
78
|
+
[Test]
|
79
|
+
public void FiveByFiveNonTerritoryDueToTooHighCoordinate()
|
80
|
+
{
|
81
|
+
var board = new GoCounting(boardFiveByFive);
|
82
|
+
Assert.That(board.TerritoryFor(new Point(1, 5)), Is.Null);
|
83
|
+
}
|
84
|
+
|
85
|
+
[Ignore("Remove to run test")]
|
86
|
+
[Test]
|
87
|
+
public void MinimalBoardWithNoTerritories()
|
88
|
+
{
|
89
|
+
var input = "B";
|
90
|
+
var board = new GoCounting(input);
|
91
|
+
|
92
|
+
var expected = new Dictionary<GoCounting.Player, IEnumerable<Point>>();
|
93
|
+
|
94
|
+
Assert.That(board.Territories(), Is.EquivalentTo(expected));
|
95
|
+
}
|
96
|
+
|
97
|
+
[Ignore("Remove to run test")]
|
98
|
+
[Test]
|
99
|
+
public void OneTerritoryCoveringTheWholeBoard()
|
100
|
+
{
|
101
|
+
var input = " ";
|
102
|
+
var board = new GoCounting(input);
|
103
|
+
|
104
|
+
var expected = new Dictionary<GoCounting.Player, IEnumerable<Point>>
|
105
|
+
{
|
106
|
+
[GoCounting.Player.None] = new[] { new Point(0, 0) }
|
107
|
+
};
|
108
|
+
|
109
|
+
Assert.That(board.Territories(), Is.EquivalentTo(expected));
|
110
|
+
}
|
111
|
+
|
112
|
+
[Ignore("Remove to run test")]
|
113
|
+
[Test]
|
114
|
+
public void TwoTerritoriesOnRectangularBoard()
|
115
|
+
{
|
116
|
+
var input = string.Join("\n", new[] { " BW ", " BW " });
|
117
|
+
var board = new GoCounting(input);
|
118
|
+
|
119
|
+
var expected = new Dictionary<GoCounting.Player, IEnumerable<Point>>
|
120
|
+
{
|
121
|
+
[GoCounting.Player.Black] = new[] { new Point(0, 0), new Point(0, 1) },
|
122
|
+
[GoCounting.Player.White] = new[] { new Point(3, 0), new Point(3, 1) }
|
123
|
+
};
|
124
|
+
|
125
|
+
Assert.That(board.Territories(), Is.EquivalentTo(expected));
|
126
|
+
}
|
127
|
+
}
|
data/tracks/fsharp/config.json
CHANGED
@@ -65,13 +65,6 @@
|
|
65
65
|
"Floating-point numbers"
|
66
66
|
]
|
67
67
|
},
|
68
|
-
{
|
69
|
-
"slug": "bank-account",
|
70
|
-
"difficulty": 2,
|
71
|
-
"topics": [
|
72
|
-
"Optional values"
|
73
|
-
]
|
74
|
-
},
|
75
68
|
{
|
76
69
|
"slug": "grains",
|
77
70
|
"difficulty": 2,
|
@@ -557,6 +550,14 @@
|
|
557
550
|
"Transforming"
|
558
551
|
]
|
559
552
|
},
|
553
|
+
{
|
554
|
+
"slug": "bank-account",
|
555
|
+
"difficulty": 5,
|
556
|
+
"topics": [
|
557
|
+
"Optional values",
|
558
|
+
"Concurrency"
|
559
|
+
]
|
560
|
+
},
|
560
561
|
{
|
561
562
|
"slug": "markdown",
|
562
563
|
"difficulty": 5,
|
@@ -574,6 +575,13 @@
|
|
574
575
|
"Transforming"
|
575
576
|
]
|
576
577
|
},
|
578
|
+
{
|
579
|
+
"slug": "book-store",
|
580
|
+
"difficulty": 5,
|
581
|
+
"topics": [
|
582
|
+
"Recursion"
|
583
|
+
]
|
584
|
+
},
|
577
585
|
{
|
578
586
|
"slug": "tournament",
|
579
587
|
"difficulty": 6,
|
@@ -1,6 +1,16 @@
|
|
1
|
-
|
1
|
+
## Learning F#
|
2
2
|
|
3
|
-
|
4
|
-
* [F#
|
5
|
-
* [Try F#](http://www.tryfsharp.org/Learn)
|
6
|
-
* [F# Foundation](http://fsharp.org/)
|
3
|
+
### Websites
|
4
|
+
* The [F# for Fun and Profit](http://fsharpforfunandprofit.com/) website has tons of great F# content. From a gentle introduction to the world of F# to more advanced concepts, it's all covered. Bonus: all content is also available as an [eBook](https://www.gitbook.com/book/swlaschin/fsharpforfunandprofit/details).
|
5
|
+
* The [Try F#](http://www.tryfsharp.org/Learn) website has a great [interactive tutorial](http://www.tryfsharp.org/Learn/getting-started) that allows you to write, compile and execute F# code directly in the browser.
|
6
|
+
* The [F# Foundation](http://fsharp.org/) is a non-profit organisation which aim is to promote F#. The website has lots of links to great F# content. Perhaps even more interesting is their [mentorship program](http://fsharp.org/mentorship/index.html), where you can apply to learn F# from an experienced F# mentor.
|
7
|
+
|
8
|
+
### Videos
|
9
|
+
* [F# for the Practical Developer](https://www.youtube.com/watch?v=7z_q06HQLes&t=2070s) is a nice introduction to F#.
|
10
|
+
* [F# - Why you should give an F](https://www.youtube.com/watch?v=kKkFabSzZvU) has Daniel Chambers give a sweet introduction into F#, neatly highlighting most F#'s features.
|
11
|
+
* [Dr. Don Syme - Introduction to F#](https://channel9.msdn.com/Series/C9-Lectures-Dr-Don-Syme-Introduction-to-F-/C9-Lectures-Dr-Don-Syme-Introduction-to-F-1-of-3) has Don Syme, the designer of F#, give an introduction to F#.
|
12
|
+
* If you're a C# developer, you might like [F# for C# developers](https://vimeo.com/78908217) by Phil Trelford.
|
13
|
+
|
14
|
+
### Books
|
15
|
+
* [Beginning F# 4.0](https://books.google.nl/books?id=puQgDAAAQBAJ&redir_esc=y)
|
16
|
+
* [Real-World Functional Programming](https://books.google.nl/books?id=KfooAQAAMAAJ&q=isbn:1933988924&dq=isbn:1933988924&hl=en&sa=X&ved=0ahUKEwj-4eCii43RAhWdYFAKHdmnAEIQ6AEIHDAA)
|
@@ -1,5 +1,17 @@
|
|
1
|
-
##
|
2
|
-
F# projects (.fsproj) can be linted for further code analysis via a third party tool - [FSharpLint](https://github.com/fsprojects/FSharpLint).
|
1
|
+
## Recommended Learning Resources
|
3
2
|
|
4
|
-
|
3
|
+
### Blogs
|
4
|
+
* Sergey Tihon's [F# Weekly blog](https://sergeytihon.wordpress.com/) is a weekly-updated website that lists recent F# news and articles.
|
5
|
+
* Mark Seemann's [blog](http://blog.ploeh.dk/) also has lots of interesting F# articles, usually focusing on how to design your application.
|
5
6
|
|
7
|
+
### Social media
|
8
|
+
* [StackOverflow ](http://stackoverflow.com/questions/tagged/f%23) can be used to search for your problem and see if it has been answered already. You can also ask and answer questions.
|
9
|
+
* The [F# channel](https://functionalprogramming.slack.com/messages/fsharp/) of the [functionalprogramming slack account](https://functionalprogramming.slack.com/). To join, go to [fpchat.com](http://fpchat.com/).
|
10
|
+
* The [F# slack account](https://fsharp.slack.com). To join, you have to [become a member of the F# foundation](http://fsharp.org/guides/slack/).
|
11
|
+
* [/r/fsharp](https://www.reddit.com/r/fsharp) is the F# subreddit.
|
12
|
+
|
13
|
+
### Videos
|
14
|
+
* The [Community for F#](https://www.youtube.com/channel/UCCQPh0mSMaVpRcKUeWPotSA/feed) YouTube channel has got lots of F# videos.
|
15
|
+
|
16
|
+
### Books
|
17
|
+
* [Expert F# 4.0](https://books.google.nl/books?id=L_0PogEACAAJ&dq=isbn:1484207424&hl=en&sa=X&ved=0ahUKEwjs__-hi43RAhWIMFAKHUJPASwQ6AEIHDAA)
|
@@ -12,11 +12,13 @@ let ``Returns empty balance after opening`` () =
|
|
12
12
|
[<Test>]
|
13
13
|
[<Ignore("Remove to run test")>]
|
14
14
|
let ``Check basic balance`` () =
|
15
|
-
let
|
16
|
-
let openingBalance =
|
15
|
+
let account = mkBankAccount() |> openAccount
|
16
|
+
let openingBalance = account |> getBalance
|
17
17
|
|
18
|
-
let
|
19
|
-
|
18
|
+
let updatedBalance =
|
19
|
+
account
|
20
|
+
|> updateBalance 10.0
|
21
|
+
|> getBalance
|
20
22
|
|
21
23
|
Assert.That(openingBalance, Is.EqualTo(Some 0.0))
|
22
24
|
Assert.That(updatedBalance, Is.EqualTo(Some 10.0))
|
@@ -24,14 +26,18 @@ let ``Check basic balance`` () =
|
|
24
26
|
[<Test>]
|
25
27
|
[<Ignore("Remove to run test")>]
|
26
28
|
let ``Balance can increment or decrement`` () =
|
27
|
-
let
|
28
|
-
let openingBalance =
|
29
|
+
let account = mkBankAccount() |> openAccount
|
30
|
+
let openingBalance = account |> getBalance
|
29
31
|
|
30
|
-
let
|
31
|
-
|
32
|
+
let addedBalance =
|
33
|
+
account
|
34
|
+
|> updateBalance 10.0
|
35
|
+
|> getBalance
|
32
36
|
|
33
|
-
let
|
34
|
-
|
37
|
+
let subtractedBalance =
|
38
|
+
account
|
39
|
+
|> updateBalance -15.0
|
40
|
+
|> getBalance
|
35
41
|
|
36
42
|
Assert.That(openingBalance, Is.EqualTo(Some 0.0))
|
37
43
|
Assert.That(addedBalance, Is.EqualTo(Some 10.0))
|
@@ -45,4 +51,26 @@ let ``Account can be closed`` () =
|
|
45
51
|
|> openAccount
|
46
52
|
|> closeAccount
|
47
53
|
|
48
|
-
Assert.That(account |> getBalance, Is.EqualTo(None))
|
54
|
+
Assert.That(account |> getBalance, Is.EqualTo(None))
|
55
|
+
|
56
|
+
[<Test>]
|
57
|
+
[<Ignore("Remove to run test")>]
|
58
|
+
let ``Account can be updated from multiple threads`` () =
|
59
|
+
let account =
|
60
|
+
mkBankAccount()
|
61
|
+
|> openAccount
|
62
|
+
|
63
|
+
let updateAccountAsync =
|
64
|
+
async {
|
65
|
+
account
|
66
|
+
|> updateBalance 1.0
|
67
|
+
|> ignore
|
68
|
+
}
|
69
|
+
|
70
|
+
updateAccountAsync
|
71
|
+
|> List.replicate 1000
|
72
|
+
|> Async.Parallel
|
73
|
+
|> Async.RunSynchronously
|
74
|
+
|> ignore
|
75
|
+
|
76
|
+
Assert.That(account |> getBalance, Is.EqualTo(Some 1000.0))
|
@@ -1,24 +1,29 @@
|
|
1
1
|
module BankAccount
|
2
2
|
|
3
|
-
|
4
|
-
| Open of float
|
5
|
-
| Closed
|
3
|
+
open System
|
6
4
|
|
7
|
-
|
5
|
+
type BankAccount() =
|
6
|
+
member val Lock = new Object()
|
7
|
+
member val Balance: float option = None with get,set
|
8
8
|
|
9
|
-
let
|
10
|
-
function
|
11
|
-
| Open x -> Open x
|
12
|
-
| Closed -> Open 0.0
|
9
|
+
let mkBankAccount() = BankAccount()
|
13
10
|
|
14
|
-
let
|
11
|
+
let openAccount (account: BankAccount) =
|
12
|
+
lock account.Lock (fun () ->
|
13
|
+
account.Balance <- Some 0.0
|
14
|
+
account
|
15
|
+
)
|
15
16
|
|
16
|
-
let
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
let closeAccount (account: BankAccount) =
|
18
|
+
lock account.Lock (fun () ->
|
19
|
+
account.Balance <- None
|
20
|
+
account
|
21
|
+
)
|
20
22
|
|
21
|
-
let
|
22
|
-
|
23
|
-
|
24
|
-
|
23
|
+
let getBalance (account: BankAccount) = account.Balance
|
24
|
+
|
25
|
+
let updateBalance change (account: BankAccount) =
|
26
|
+
lock account.Lock (fun () ->
|
27
|
+
account.Balance <- Option.map ((+) change) account.Balance
|
28
|
+
account
|
29
|
+
)
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module BookStoreTest
|
2
|
+
|
3
|
+
open System
|
4
|
+
open NUnit.Framework
|
5
|
+
open BookStore
|
6
|
+
|
7
|
+
[<Test>]
|
8
|
+
let ``Basket with single book`` () =
|
9
|
+
Assert.That(calculateTotalCost [1], Is.EqualTo(8))
|
10
|
+
|
11
|
+
[<Ignore("Remove to run test")>]
|
12
|
+
[<Test>]
|
13
|
+
let ``Basket with two of same book`` () =
|
14
|
+
Assert.That(calculateTotalCost [2; 2], Is.EqualTo(16))
|
15
|
+
|
16
|
+
[<Ignore("Remove to run test")>]
|
17
|
+
[<Test>]
|
18
|
+
let ``Empty basket`` () =
|
19
|
+
Assert.That(calculateTotalCost [], Is.EqualTo(0))
|
20
|
+
|
21
|
+
[<Ignore("Remove to run test")>]
|
22
|
+
[<Test>]
|
23
|
+
let ``Basket with two different books`` () =
|
24
|
+
Assert.That(calculateTotalCost [1; 2], Is.EqualTo(15.2))
|
25
|
+
|
26
|
+
[<Ignore("Remove to run test")>]
|
27
|
+
[<Test>]
|
28
|
+
let ``Basket with three different books`` () =
|
29
|
+
Assert.That(calculateTotalCost [1; 2; 3], Is.EqualTo(21.6))
|
30
|
+
|
31
|
+
[<Ignore("Remove to run test")>]
|
32
|
+
[<Test>]
|
33
|
+
let ``Basket with four different books`` () =
|
34
|
+
Assert.That(calculateTotalCost [1; 2; 3; 4], Is.EqualTo(25.6))
|
35
|
+
|
36
|
+
[<Ignore("Remove to run test")>]
|
37
|
+
[<Test>]
|
38
|
+
let ``Basket with five different books`` () =
|
39
|
+
Assert.That(calculateTotalCost [1; 2; 3; 4; 5], Is.EqualTo(30))
|
40
|
+
|
41
|
+
[<Ignore("Remove to run test")>]
|
42
|
+
[<Test>]
|
43
|
+
let ``Basket with eight books`` () =
|
44
|
+
Assert.That(calculateTotalCost [1; 1; 2; 2; 3; 3; 4; 5], Is.EqualTo(51.20))
|
45
|
+
|
46
|
+
[<Ignore("Remove to run test")>]
|
47
|
+
[<Test>]
|
48
|
+
let ``Basket with nine books`` () =
|
49
|
+
Assert.That(calculateTotalCost [1; 1; 2; 2; 3; 3; 4; 4; 5], Is.EqualTo(55.60))
|
50
|
+
|
51
|
+
[<Ignore("Remove to run test")>]
|
52
|
+
[<Test>]
|
53
|
+
let ``Basket with ten books`` () =
|
54
|
+
Assert.That(calculateTotalCost [1; 1; 2; 2; 3; 3; 4; 4; 5; 5], Is.EqualTo(60))
|
55
|
+
|
56
|
+
[<Ignore("Remove to run test")>]
|
57
|
+
[<Test>]
|
58
|
+
let ``Basket with eleven books`` () =
|
59
|
+
Assert.That(calculateTotalCost [1; 1; 2; 2; 3; 3; 4; 4; 5; 5; 1], Is.EqualTo(68))
|
60
|
+
|
61
|
+
[<Ignore("Remove to run test")>]
|
62
|
+
[<Test>]
|
63
|
+
let ``Basket with twelve books`` () =
|
64
|
+
Assert.That(calculateTotalCost [1; 1; 2; 2; 3; 3; 4; 4; 5; 5; 1; 2], Is.EqualTo(75.20))
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module BookStore
|
2
|
+
|
3
|
+
let private costPerGroup groupSize =
|
4
|
+
let discountPercentage =
|
5
|
+
match groupSize with
|
6
|
+
| 1 -> 0.
|
7
|
+
| 2 -> 5.
|
8
|
+
| 3 -> 10.
|
9
|
+
| 4 -> 20.
|
10
|
+
| 5 -> 25.
|
11
|
+
| _ -> failwith "Invalid group size"
|
12
|
+
|
13
|
+
8. * (groupSize |> float) * (100. - discountPercentage) / 100.
|
14
|
+
|
15
|
+
let private remove n list =
|
16
|
+
let rec removeTail n list acc =
|
17
|
+
match list with
|
18
|
+
| x::xs when x = n -> List.append (List.rev acc) xs
|
19
|
+
| x::xs -> removeTail n xs (x::acc)
|
20
|
+
| [] -> List.rev acc
|
21
|
+
removeTail n list []
|
22
|
+
|
23
|
+
let rec private calculateTotalCostHelper books priceSoFar =
|
24
|
+
match books |> List.length with
|
25
|
+
| 0 -> priceSoFar
|
26
|
+
| _ ->
|
27
|
+
let groups =
|
28
|
+
books
|
29
|
+
|> List.groupBy id
|
30
|
+
|> List.map fst
|
31
|
+
|
32
|
+
let prices =
|
33
|
+
[1 .. groups |> List.length]
|
34
|
+
|> List.map (fun i ->
|
35
|
+
let itemsToRemove =
|
36
|
+
groups
|
37
|
+
|> List.take i
|
38
|
+
|
39
|
+
let remaining =
|
40
|
+
itemsToRemove
|
41
|
+
|> List.fold (fun state t ->
|
42
|
+
remove t state)
|
43
|
+
books
|
44
|
+
|
45
|
+
calculateTotalCostHelper remaining (priceSoFar + costPerGroup i)
|
46
|
+
)
|
47
|
+
|
48
|
+
prices
|
49
|
+
|> List.min
|
50
|
+
|
51
|
+
let calculateTotalCost books =
|
52
|
+
calculateTotalCostHelper books 0.
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: trackler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.5.
|
4
|
+
version: 2.0.5.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Katrina Owen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-12-
|
11
|
+
date: 2016-12-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rubyzip
|
@@ -1333,6 +1333,8 @@ files:
|
|
1333
1333
|
- tracks/csharp/exercises/food-chain/FoodChainTest.cs
|
1334
1334
|
- tracks/csharp/exercises/gigasecond/Example.cs
|
1335
1335
|
- tracks/csharp/exercises/gigasecond/GigasecondTest.cs
|
1336
|
+
- tracks/csharp/exercises/go-counting/Example.cs
|
1337
|
+
- tracks/csharp/exercises/go-counting/GoCountingTest.cs
|
1336
1338
|
- tracks/csharp/exercises/grade-school/Example.cs
|
1337
1339
|
- tracks/csharp/exercises/grade-school/GradeSchoolTest.cs
|
1338
1340
|
- tracks/csharp/exercises/grains/Example.cs
|
@@ -2420,6 +2422,8 @@ files:
|
|
2420
2422
|
- tracks/fsharp/exercises/binary-search/Example.fs
|
2421
2423
|
- tracks/fsharp/exercises/bob/BobTest.fs
|
2422
2424
|
- tracks/fsharp/exercises/bob/Example.fs
|
2425
|
+
- tracks/fsharp/exercises/book-store/BookStoreTest.fs
|
2426
|
+
- tracks/fsharp/exercises/book-store/Example.fs
|
2423
2427
|
- tracks/fsharp/exercises/bowling/BowlingTest.fs
|
2424
2428
|
- tracks/fsharp/exercises/bowling/Example.fs
|
2425
2429
|
- tracks/fsharp/exercises/bracket-push/BracketPushTest.fs
|