trackler 2.2.1.61 → 2.2.1.62
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/trackler/version.rb +1 -1
- data/problem-specifications/exercises/forth/canonical-data.json +1 -7
- data/problem-specifications/exercises/meetup/description.md +16 -12
- data/problem-specifications/exercises/sum-of-multiples/description.md +3 -3
- data/tracks/c/config.json +14 -1
- data/tracks/c/exercises/anagram/src/anagram.h +19 -4
- data/tracks/c/exercises/anagram/src/example.c +27 -40
- data/tracks/c/exercises/anagram/test/test_anagram.c +100 -152
- data/tracks/c/exercises/sublist/README.md +53 -0
- data/tracks/c/exercises/sublist/makefile +15 -0
- data/tracks/c/exercises/sublist/src/example.c +63 -0
- data/tracks/c/exercises/sublist/src/sublist.h +17 -0
- data/tracks/c/exercises/sublist/test/test_sublist.c +243 -0
- data/tracks/c/exercises/sublist/test/vendor/unity.c +1300 -0
- data/tracks/c/exercises/sublist/test/vendor/unity.h +274 -0
- data/tracks/c/exercises/sublist/test/vendor/unity_internals.h +701 -0
- data/tracks/ecmascript/config.json +56 -0
- data/tracks/ecmascript/exercises/rectangles/README.md +95 -0
- data/tracks/ecmascript/exercises/rectangles/example.js +38 -0
- data/tracks/ecmascript/exercises/rectangles/package.json +72 -0
- data/tracks/ecmascript/exercises/rectangles/rectangles.spec.js +147 -0
- data/tracks/ecmascript/exercises/rotational-cipher/README.md +66 -0
- data/tracks/ecmascript/exercises/rotational-cipher/example.js +16 -0
- data/tracks/ecmascript/exercises/rotational-cipher/package.json +72 -0
- data/tracks/ecmascript/exercises/rotational-cipher/rotational-cipher.spec.js +73 -0
- data/tracks/ecmascript/exercises/spiral-matrix/README.md +59 -0
- data/tracks/ecmascript/exercises/spiral-matrix/example.js +26 -0
- data/tracks/ecmascript/exercises/spiral-matrix/package.json +72 -0
- data/tracks/ecmascript/exercises/spiral-matrix/spiral-matrix.spec.js +55 -0
- data/tracks/ecmascript/exercises/transpose/README.md +94 -0
- data/tracks/ecmascript/exercises/transpose/example.js +12 -0
- data/tracks/ecmascript/exercises/transpose/package.json +71 -0
- data/tracks/ecmascript/exercises/transpose/transpose.spec.js +121 -0
- data/tracks/go/exercises/allergies/.meta/gen.go +88 -0
- data/tracks/go/exercises/allergies/allergies_test.go +19 -46
- data/tracks/go/exercises/allergies/cases_test.go +60 -0
- data/tracks/go/exercises/bob/bob.go +0 -2
- data/tracks/go/exercises/forth/cases_test.go +2 -7
- data/tracks/go/exercises/nucleotide-count/nucleotide_count.go +0 -2
- data/tracks/go/exercises/pangram/.meta/gen.go +54 -0
- data/tracks/go/exercises/pangram/cases_test.go +62 -0
- data/tracks/go/exercises/pangram/example.go +3 -5
- data/tracks/go/exercises/pangram/pangram_test.go +3 -24
- data/tracks/go/exercises/rna-transcription/rna_transcription.go +0 -2
- data/tracks/go/exercises/robot-name/bonus_example.go +0 -2
- data/tracks/java/exercises/acronym/.meta/src/reference/java/Acronym.java +3 -3
- data/tracks/lfe/config.json +4 -3
- data/tracks/perl6/docs/SNIPPET.txt +8 -3
- data/tracks/perl6/exercises/allergies/Allergies.pm6 +1 -1
- data/tracks/perl6/exercises/allergies/Example.pm6 +1 -1
- data/tracks/perl6/exercises/allergies/allergies.t +10 -2
- data/tracks/perl6/exercises/allergies/example.yaml +10 -2
- data/tracks/perl6/exercises/leap/Example.pm6 +1 -1
- data/tracks/perl6/exercises/leap/Leap.pm6 +1 -1
- data/tracks/perl6/exercises/leap/example.yaml +10 -2
- data/tracks/perl6/exercises/leap/leap.t +10 -2
- data/tracks/perl6/exercises/luhn/Example.pm6 +1 -1
- data/tracks/perl6/exercises/luhn/Luhn.pm6 +1 -1
- data/tracks/perl6/exercises/luhn/example.yaml +10 -2
- data/tracks/perl6/exercises/luhn/luhn.t +10 -2
- data/tracks/perl6/exercises/pangram/Example.pm6 +1 -1
- data/tracks/perl6/exercises/pangram/Pangram.pm6 +1 -1
- data/tracks/perl6/exercises/pangram/example.yaml +12 -6
- data/tracks/perl6/exercises/pangram/pangram.t +9 -3
- data/tracks/python/config.json +38 -0
- data/tracks/python/exercises/complex-numbers/complex_numbers_test.py +81 -66
- data/tracks/python/exercises/connect/README.md +44 -0
- data/tracks/python/exercises/connect/connect.py +7 -0
- data/tracks/python/exercises/connect/connect_test.py +118 -0
- data/tracks/python/exercises/connect/example.py +61 -0
- data/tracks/python/exercises/go-counting/README.md +44 -0
- data/tracks/python/exercises/go-counting/example.py +62 -0
- data/tracks/python/exercises/go-counting/go_counting.py +38 -0
- data/tracks/python/exercises/go-counting/go_counting_test.py +92 -0
- data/tracks/python/exercises/parallel-letter-frequency/README.md +24 -0
- data/tracks/python/exercises/parallel-letter-frequency/example.py +56 -0
- data/tracks/python/exercises/parallel-letter-frequency/parallel_letter_frequency.py +2 -0
- data/tracks/python/exercises/parallel-letter-frequency/parallel_letter_frequency_test.py +61 -0
- data/tracks/python/exercises/strain/README.md +3 -1
- data/tracks/python/exercises/strain/strain.py +2 -2
- data/tracks/rust/.travis.yml +1 -0
- data/tracks/rust/README.md +2 -0
- data/tracks/rust/_test/check-exercises.sh +41 -54
- data/tracks/rust/_test/ensure-readmes-are-updated.sh +40 -0
- data/tracks/rust/bin/test-exercise +84 -0
- data/tracks/rust/exercises/alphametics/.meta/test-in-release-mode +2 -0
- data/tracks/rust/exercises/alphametics/src/lib.rs +1 -1
- data/tracks/rust/exercises/dominoes/README.md +3 -3
- data/tracks/rust/exercises/isbn-verifier/README.md +3 -2
- data/tracks/rust/exercises/nucleotide-count/README.md +2 -2
- data/tracks/rust/exercises/perfect-numbers/src/lib.rs +10 -0
- data/tracks/rust/exercises/phone-number/README.md +1 -1
- data/tracks/rust/exercises/pythagorean-triplet/src/lib.rs +3 -0
- data/tracks/rust/exercises/rectangles/README.md +9 -9
- data/tracks/rust/exercises/two-bucket/README.md +7 -7
- data/tracks/typescript/config.json +16 -0
- data/tracks/typescript/exercises/diamond/README.md +84 -0
- data/tracks/typescript/exercises/diamond/diamond.example.ts +51 -0
- data/tracks/typescript/exercises/diamond/diamond.test.ts +35 -0
- data/tracks/typescript/exercises/diamond/diamond.ts +0 -0
- data/tracks/typescript/exercises/diamond/package.json +36 -0
- data/tracks/typescript/exercises/diamond/tsconfig.json +22 -0
- data/tracks/typescript/exercises/diamond/tslint.json +127 -0
- data/tracks/typescript/exercises/diamond/yarn.lock +2624 -0
- metadata +53 -2
@@ -0,0 +1,61 @@
|
|
1
|
+
|
2
|
+
class ConnectGame:
|
3
|
+
|
4
|
+
directions = [(0, 1), (0, -1), (1, 0), (-1, 0), (1, -1), (-1, 1)]
|
5
|
+
white = "O"
|
6
|
+
black = "X"
|
7
|
+
none = ""
|
8
|
+
|
9
|
+
def __init__(self, lines):
|
10
|
+
self.board = self.make_board(lines)
|
11
|
+
assert len(self.board) > 0
|
12
|
+
|
13
|
+
self.width = len(self.board[0])
|
14
|
+
self.height = len(self.board)
|
15
|
+
assert self.width > 0 and self.height > 0
|
16
|
+
|
17
|
+
for l in self.board:
|
18
|
+
assert len(l) == self.width
|
19
|
+
|
20
|
+
def valid(self, x, y):
|
21
|
+
return x >= 0 and x < self.width and y >= 0 and y < self.height
|
22
|
+
|
23
|
+
def make_board(self, lines):
|
24
|
+
return ["".join(l.split()) for l in lines.splitlines()]
|
25
|
+
|
26
|
+
def player_reach_dest(self, player, x, y):
|
27
|
+
if player == self.black:
|
28
|
+
return x == self.width - 1
|
29
|
+
if player == self.white:
|
30
|
+
return y == self.height - 1
|
31
|
+
|
32
|
+
def walk_board(self, player, x, y, visited=[]):
|
33
|
+
if (x, y) in visited:
|
34
|
+
return False
|
35
|
+
|
36
|
+
if (not self.valid(x, y)) or self.board[y][x] != player:
|
37
|
+
return False
|
38
|
+
|
39
|
+
if self.player_reach_dest(player, x, y):
|
40
|
+
return True
|
41
|
+
|
42
|
+
for d in self.directions:
|
43
|
+
if self.walk_board(player, x + d[0], y + d[1], visited + [(x, y)]):
|
44
|
+
return True
|
45
|
+
|
46
|
+
def check_player_is_winner(self, player):
|
47
|
+
if player == self.black:
|
48
|
+
for y in range(self.height):
|
49
|
+
if self.walk_board(player, 0, y):
|
50
|
+
return True
|
51
|
+
if player == self.white:
|
52
|
+
for x in range(self.width):
|
53
|
+
if self.walk_board(player, x, 0):
|
54
|
+
return True
|
55
|
+
|
56
|
+
def get_winner(self):
|
57
|
+
if self.check_player_is_winner(self.black):
|
58
|
+
return self.black
|
59
|
+
if self.check_player_is_winner(self.white):
|
60
|
+
return self.white
|
61
|
+
return self.none
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# Go Counting
|
2
|
+
|
3
|
+
Count the scored points on a Go board.
|
4
|
+
|
5
|
+
In the game of go (also known as baduk, igo, cờ vây and wéiqí) points
|
6
|
+
are gained by completely encircling empty intersections with your
|
7
|
+
stones. The encircled intersections of a player are known as its
|
8
|
+
territory.
|
9
|
+
|
10
|
+
Write a function that determines the territory of each player. You may
|
11
|
+
assume that any stones that have been stranded in enemy territory have
|
12
|
+
already been taken off the board.
|
13
|
+
|
14
|
+
Multiple empty intersections may be encircled at once and for encircling
|
15
|
+
only horizontal and vertical neighbours count. In the following diagram
|
16
|
+
the stones which matter are marked "O" and the stones that don't are
|
17
|
+
marked "I" (ignored). Empty spaces represent empty intersections.
|
18
|
+
|
19
|
+
```
|
20
|
+
+----+
|
21
|
+
|IOOI|
|
22
|
+
|O O|
|
23
|
+
|O OI|
|
24
|
+
|IOI |
|
25
|
+
+----+
|
26
|
+
```
|
27
|
+
|
28
|
+
To be more precise an empty intersection is part of a player's territory
|
29
|
+
if all of its neighbours are either stones of that player or empty
|
30
|
+
intersections that are part of that player's territory.
|
31
|
+
|
32
|
+
For more information see
|
33
|
+
[wikipedia](https://en.wikipedia.org/wiki/Go_%28game%29) or [Sensei's
|
34
|
+
Library](http://senseis.xmp.net/).
|
35
|
+
|
36
|
+
## Submitting Exercises
|
37
|
+
|
38
|
+
Note that, when trying to submit an exercise, make sure the solution is in the `exercism/python/<exerciseName>` directory.
|
39
|
+
|
40
|
+
For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit <path_to_exercism_dir>/python/bob/bob.py`.
|
41
|
+
|
42
|
+
## Submitting Incomplete Solutions
|
43
|
+
It's possible to submit an incomplete solution so you can see how others have completed the
|
44
|
+
exercise.
|
@@ -0,0 +1,62 @@
|
|
1
|
+
|
2
|
+
BLACK = "B"
|
3
|
+
WHITE = "W"
|
4
|
+
NONE = ""
|
5
|
+
STONES = [BLACK, WHITE]
|
6
|
+
DIRECTIONS = [(0, 1), (0, -1), (1, 0), (-1, 0)]
|
7
|
+
|
8
|
+
|
9
|
+
class Board:
|
10
|
+
def __init__(self, board):
|
11
|
+
self.board = board.splitlines()
|
12
|
+
self.width = len(self.board[0])
|
13
|
+
self.height = len(self.board)
|
14
|
+
|
15
|
+
def valid(self, x, y):
|
16
|
+
return x >= 0 and x < self.width and y >= 0 and y < self.height
|
17
|
+
|
18
|
+
def walk(self, x, y,
|
19
|
+
visited_territory=[],
|
20
|
+
visited_coords=[],
|
21
|
+
visited_stones=[]):
|
22
|
+
if not (x, y) in visited_coords and self.valid(x, y):
|
23
|
+
s = self.board[y][x]
|
24
|
+
if s in STONES:
|
25
|
+
if s not in visited_stones:
|
26
|
+
return (visited_territory, visited_stones + [s])
|
27
|
+
else: # s is empty
|
28
|
+
for d in DIRECTIONS:
|
29
|
+
visited = self.walk(x + d[0], y + d[1],
|
30
|
+
visited_territory + [(x, y)],
|
31
|
+
visited_coords + [(x, y)],
|
32
|
+
visited_stones)
|
33
|
+
visited_territory = visited[0]
|
34
|
+
visited_stones = visited[1]
|
35
|
+
|
36
|
+
return (visited_territory, visited_stones)
|
37
|
+
|
38
|
+
def territoryFor(self, coord):
|
39
|
+
assert len(coord) == 2
|
40
|
+
x, y = coord[0], coord[1]
|
41
|
+
if not self.valid(x, y) or self.board[y][x] in STONES:
|
42
|
+
return (NONE, set())
|
43
|
+
|
44
|
+
visited_territory, visited_stones = self.walk(x, y)
|
45
|
+
result = set(visited_territory)
|
46
|
+
|
47
|
+
if len(visited_stones) == 1:
|
48
|
+
return (visited_stones[0], result)
|
49
|
+
return (NONE, result)
|
50
|
+
|
51
|
+
def territories(self):
|
52
|
+
owners = STONES + [NONE]
|
53
|
+
result = dict([(owner, set()) for owner in owners])
|
54
|
+
visited = set()
|
55
|
+
for y in range(self.height):
|
56
|
+
for x in range(self.width):
|
57
|
+
if not (x, y) in visited:
|
58
|
+
owner, owned_territories = self.territoryFor((x, y))
|
59
|
+
result[owner].update(owned_territories)
|
60
|
+
visited.update(owned_territories)
|
61
|
+
|
62
|
+
return result
|
@@ -0,0 +1,38 @@
|
|
1
|
+
|
2
|
+
class Board:
|
3
|
+
"""Count territories of each player in a Go game
|
4
|
+
|
5
|
+
Args:
|
6
|
+
board (list[str]): A two-dimensional Go board
|
7
|
+
"""
|
8
|
+
|
9
|
+
def __init__(self, board):
|
10
|
+
pass
|
11
|
+
|
12
|
+
def territoryFor(self, coord):
|
13
|
+
"""Find the owner and the territories given a coordinate on
|
14
|
+
the board
|
15
|
+
|
16
|
+
Args:
|
17
|
+
coord ((int,int)): Coordinate on the board
|
18
|
+
|
19
|
+
Returns:
|
20
|
+
(str, set): A tuple, the first element being the owner
|
21
|
+
of that area. One of "W", "B", "". The
|
22
|
+
second being a set of coordinates, representing
|
23
|
+
the owner's territories.
|
24
|
+
"""
|
25
|
+
pass
|
26
|
+
|
27
|
+
def territories(self):
|
28
|
+
"""Find the owners and the territories of the whole board
|
29
|
+
|
30
|
+
Args:
|
31
|
+
none
|
32
|
+
|
33
|
+
Returns:
|
34
|
+
dict(str, set): A dictionary whose key being the owner
|
35
|
+
, i.e. "W", "B", "". The value being a set
|
36
|
+
of coordinates owned by the owner.
|
37
|
+
"""
|
38
|
+
pass
|
@@ -0,0 +1,92 @@
|
|
1
|
+
import unittest
|
2
|
+
import gocounting
|
3
|
+
|
4
|
+
|
5
|
+
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.1.0
|
6
|
+
|
7
|
+
board5x5 = "\n".join([
|
8
|
+
" B ",
|
9
|
+
" B B ",
|
10
|
+
"B W B",
|
11
|
+
" W W ",
|
12
|
+
" W "
|
13
|
+
])
|
14
|
+
|
15
|
+
board9x9 = "\n".join([
|
16
|
+
" B B ",
|
17
|
+
"B B B",
|
18
|
+
"WBBBWBBBW",
|
19
|
+
"W W W W W",
|
20
|
+
" ",
|
21
|
+
" W W W W ",
|
22
|
+
"B B B B",
|
23
|
+
" W BBB W ",
|
24
|
+
" B B "
|
25
|
+
])
|
26
|
+
|
27
|
+
|
28
|
+
class GoCountingTest(unittest.TestCase):
|
29
|
+
def test_5x5_for_black(self):
|
30
|
+
board = gocounting.Board(board5x5)
|
31
|
+
stone, territory = board.territoryFor((0, 1))
|
32
|
+
self.assertEqual(stone, gocounting.BLACK)
|
33
|
+
self.assertEqual(territory, set([(0, 0), (0, 1), (1, 0)]))
|
34
|
+
|
35
|
+
def test_5x5_for_white(self):
|
36
|
+
board = gocounting.Board(board5x5)
|
37
|
+
stone, territory = board.territoryFor((2, 3))
|
38
|
+
self.assertEqual(stone, gocounting.WHITE)
|
39
|
+
self.assertEqual(territory, set([(2, 3)]))
|
40
|
+
|
41
|
+
def test_5x5_for_open_territory(self):
|
42
|
+
board = gocounting.Board(board5x5)
|
43
|
+
stone, territory = board.territoryFor((1, 4))
|
44
|
+
self.assertEqual(stone, gocounting.NONE)
|
45
|
+
self.assertEqual(territory, set([(0, 3), (0, 4), (1, 4)]))
|
46
|
+
|
47
|
+
def test_5x5_for_non_territory(self):
|
48
|
+
board = gocounting.Board(board5x5)
|
49
|
+
stone, territory = board.territoryFor((1, 1))
|
50
|
+
self.assertEqual(stone, gocounting.NONE)
|
51
|
+
self.assertEqual(territory, set())
|
52
|
+
|
53
|
+
def test_5x5_for_valid_coordinate(self):
|
54
|
+
board = gocounting.Board(board5x5)
|
55
|
+
stone, territory = board.territoryFor((-1, 1))
|
56
|
+
self.assertEqual(stone, gocounting.NONE)
|
57
|
+
self.assertEqual(territory, set())
|
58
|
+
|
59
|
+
def test_5x5_for_valid_coordinate2(self):
|
60
|
+
board = gocounting.Board(board5x5)
|
61
|
+
stone, territory = board.territoryFor((1, 5))
|
62
|
+
self.assertEqual(stone, gocounting.NONE)
|
63
|
+
self.assertEqual(territory, set())
|
64
|
+
|
65
|
+
def test_one_territory_whole_board(self):
|
66
|
+
board = gocounting.Board(" ")
|
67
|
+
territories = board.territories()
|
68
|
+
self.assertEqual(territories[gocounting.BLACK], set())
|
69
|
+
self.assertEqual(territories[gocounting.WHITE], set())
|
70
|
+
self.assertEqual(territories[gocounting.NONE], set([(0, 0)]))
|
71
|
+
|
72
|
+
def test_two_territories_rectangular_board(self):
|
73
|
+
input_board = "\n".join([
|
74
|
+
" BW ",
|
75
|
+
" BW "
|
76
|
+
])
|
77
|
+
board = gocounting.Board(input_board)
|
78
|
+
territories = board.territories()
|
79
|
+
self.assertEqual(territories[gocounting.BLACK], set([(0, 0), (0, 1)]))
|
80
|
+
self.assertEqual(territories[gocounting.WHITE], set([(3, 0), (3, 1)]))
|
81
|
+
self.assertEqual(territories[gocounting.NONE], set())
|
82
|
+
|
83
|
+
def test_9x9_for_open_territory(self):
|
84
|
+
board = gocounting.Board(board9x9)
|
85
|
+
stone, territory = board.territoryFor((0, 8))
|
86
|
+
self.assertEqual(stone, gocounting.NONE)
|
87
|
+
self.assertEqual(territory,
|
88
|
+
set([(2, 7), (2, 8), (1, 8), (0, 8), (0, 7)]))
|
89
|
+
|
90
|
+
|
91
|
+
if __name__ == '__main__':
|
92
|
+
unittest.main()
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# Parallel Letter Frequency
|
2
|
+
|
3
|
+
Count the frequency of letters in texts using parallel computation.
|
4
|
+
|
5
|
+
Parallelism is about doing things in parallel that can also be done
|
6
|
+
sequentially. A common example is counting the frequency of letters.
|
7
|
+
Create a function that returns the total frequency of each letter in a
|
8
|
+
list of texts and that employs parallelism.
|
9
|
+
|
10
|
+
The letters used consists of ASCII letters `a` to `z`, inclusive, and is case
|
11
|
+
insensitive.
|
12
|
+
|
13
|
+
## Submitting Exercises
|
14
|
+
|
15
|
+
Note that, when trying to submit an exercise, make sure the solution is in the `exercism/python/<exerciseName>` directory.
|
16
|
+
|
17
|
+
For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit <path_to_exercism_dir>/python/bob/bob.py`.
|
18
|
+
|
19
|
+
|
20
|
+
For more detailed information about running tests, code style and linting,
|
21
|
+
please see the [help page](http://exercism.io/languages/python).
|
22
|
+
|
23
|
+
## Submitting Incomplete Solutions
|
24
|
+
It's possible to submit an incomplete solution so you can see how others have completed the exercise.
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
from collections import Counter
|
3
|
+
from threading import Lock, Thread
|
4
|
+
from time import sleep
|
5
|
+
import sys
|
6
|
+
if sys.version[0] == '2':
|
7
|
+
from Queue import Queue
|
8
|
+
else:
|
9
|
+
from queue import Queue
|
10
|
+
|
11
|
+
total_workers = 3 # Maximum number of threads chosen arbitrarily
|
12
|
+
|
13
|
+
|
14
|
+
class LetterCounter(object):
|
15
|
+
|
16
|
+
def __init__(self):
|
17
|
+
self.lock = Lock()
|
18
|
+
self.value = Counter()
|
19
|
+
|
20
|
+
def add_counter(self, counter_to_add):
|
21
|
+
self.lock.acquire()
|
22
|
+
try:
|
23
|
+
self.value = self.value + counter_to_add
|
24
|
+
finally:
|
25
|
+
self.lock.release()
|
26
|
+
|
27
|
+
|
28
|
+
def count_letters(queue_of_texts, letter_to_frequency, worker_id):
|
29
|
+
while not queue_of_texts.empty():
|
30
|
+
sleep(worker_id + 1)
|
31
|
+
line_input = queue_of_texts.get()
|
32
|
+
if line_input is not None:
|
33
|
+
letters_in_line = Counter([x for x in line_input.lower() if
|
34
|
+
x.isalpha()])
|
35
|
+
letter_to_frequency.add_counter(letters_in_line)
|
36
|
+
queue_of_texts.task_done()
|
37
|
+
if line_input is None:
|
38
|
+
break
|
39
|
+
|
40
|
+
|
41
|
+
def calculate(list_of_texts):
|
42
|
+
queue_of_texts = Queue()
|
43
|
+
[queue_of_texts.put(line) for line in list_of_texts]
|
44
|
+
letter_to_frequency = LetterCounter()
|
45
|
+
threads = []
|
46
|
+
for i in range(total_workers):
|
47
|
+
worker = Thread(target=count_letters, args=(queue_of_texts,
|
48
|
+
letter_to_frequency, i))
|
49
|
+
worker.start()
|
50
|
+
threads.append(worker)
|
51
|
+
queue_of_texts.join()
|
52
|
+
for i in range(total_workers):
|
53
|
+
queue_of_texts.put(None)
|
54
|
+
for t in threads:
|
55
|
+
t.join()
|
56
|
+
return letter_to_frequency.value
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
from collections import Counter
|
3
|
+
import unittest
|
4
|
+
|
5
|
+
from parallel_letter_frequency import calculate
|
6
|
+
|
7
|
+
|
8
|
+
class ParallelLetterFrequencyTest(unittest.TestCase):
|
9
|
+
def test_one_letter(self):
|
10
|
+
actual = calculate(['a'])
|
11
|
+
expected = {'a': 1}
|
12
|
+
self.assertDictEqual(actual, expected)
|
13
|
+
|
14
|
+
def test_case_insensitivity(self):
|
15
|
+
actual = calculate(['aA'])
|
16
|
+
expected = {'a': 2}
|
17
|
+
self.assertDictEqual(actual, expected)
|
18
|
+
|
19
|
+
def test_numbers(self):
|
20
|
+
actual = calculate(['012', '345', '6789'])
|
21
|
+
expected = {}
|
22
|
+
self.assertDictEqual(actual, expected)
|
23
|
+
|
24
|
+
def test_punctuations(self):
|
25
|
+
actual = calculate(['[]\;,', './{}|', ':"<>?'])
|
26
|
+
expected = {}
|
27
|
+
self.assertDictEqual(actual, expected)
|
28
|
+
|
29
|
+
def test_whitespaces(self):
|
30
|
+
actual = calculate([' ', '\t ', '\n\n'])
|
31
|
+
expected = {}
|
32
|
+
self.assertDictEqual(actual, expected)
|
33
|
+
|
34
|
+
def test_repeated_string_with_known_frequencies(self):
|
35
|
+
letter_frequency = 3
|
36
|
+
text_input = 'abc\n' * letter_frequency
|
37
|
+
actual = calculate(text_input.split('\n'))
|
38
|
+
expected = {'a': letter_frequency, 'b': letter_frequency,
|
39
|
+
'c': letter_frequency}
|
40
|
+
self.assertDictEqual(actual, expected)
|
41
|
+
|
42
|
+
def test_multiline_text(self):
|
43
|
+
text_input = "3 Quotes from Excerism Homepage:\n" + \
|
44
|
+
"\tOne moment you feel like you're\n" + \
|
45
|
+
"getting it. The next moment you're\n" + \
|
46
|
+
"stuck.\n" + \
|
47
|
+
"\tYou know what it’s like to be fluent.\n" + \
|
48
|
+
"Suddenly you’re feeling incompetent\n" + \
|
49
|
+
"and clumsy.\n" + \
|
50
|
+
"\tHaphazard, convoluted code is\n" + \
|
51
|
+
"infuriating, not to mention costly. That\n" + \
|
52
|
+
"slapdash explosion of complexity is an\n" + \
|
53
|
+
"expensive yak shave waiting to\n" + \
|
54
|
+
"happen."
|
55
|
+
actual = calculate(text_input.split('\n'))
|
56
|
+
expected = Counter([x for x in text_input.lower() if x.isalpha()])
|
57
|
+
self.assertDictEqual(actual, expected)
|
58
|
+
|
59
|
+
|
60
|
+
if __name__ == '__main__':
|
61
|
+
unittest.main()
|