trackler 2.0.5.9 → 2.0.5.10
Sign up to get free protection for your applications and to get access to all the features.
- 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
|