Просмотр исходного кода

Merge pull request #1037 from matthewmorgan/add-exercise-parallel-letter-frequency

Add exercise parallel-letter-frequency
FridaTveit 8 лет назад
Родитель
Сommit
98e8464623
Аккаунт пользователя с таким Email не найден

+ 12
- 0
config.json Просмотреть файл

25
     },
25
     },
26
     {
26
     {
27
       "core": false,
27
       "core": false,
28
+      "difficulty": 6,
29
+      "slug": "parallel-letter-frequency",
30
+      "topics": [
31
+        "concurrency",
32
+        "maps",
33
+        "strings"
34
+      ],
35
+      "unlocked_by": "hamming",
36
+      "uuid": "38a405e8-619d-400f-b53c-2f06461fdf9d"
37
+    },
38
+    {
39
+      "core": false,
28
       "difficulty": 1,
40
       "difficulty": 1,
29
       "slug": "reverse-string",
41
       "slug": "reverse-string",
30
       "topics": [
42
       "topics": [

+ 3
- 0
exercises/parallel-letter-frequency/.meta/HINTS.md Просмотреть файл

1
+Single-threaded (non-concurrent) solutions can pass all tests [but the last.](https://www.youtube.com/watch?v=mJZZNHekEQw)  Your solution will be tested for concurrency by submitting it as a [Runnable](https://docs.oracle.com/javase/7/docs/api/java/lang/Runnable.html) to an [ExecutorService.](https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ExecutorService.html) Your solution must leverage multiple [Threads](https://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html) to pass the final test.
2
+
3
+Java documentation on [parallel streams](https://docs.oracle.com/javase/tutorial/collections/streams/parallelism.html) may provide some help.

+ 45
- 0
exercises/parallel-letter-frequency/.meta/src/reference/java/ParallelLetterFrequency.java Просмотреть файл

1
+import java.util.Collections;
2
+import java.util.HashMap;
3
+import java.util.Map;
4
+
5
+
6
+class ParallelLetterFrequency {
7
+
8
+    private static Map<Integer, Integer> letterFrequencyMap;
9
+    private final String NOT_A_LETTER = "\\P{L}+";
10
+
11
+
12
+    ParallelLetterFrequency(String letters) {
13
+        letterFrequencyMap = getMapFromLetters(letters.toLowerCase().replaceAll(NOT_A_LETTER, ""));
14
+    }
15
+
16
+    Map<Integer, Integer> getMapFromLetters(String letters) {
17
+        return letters
18
+                .chars()
19
+                .parallel()
20
+                .collect(Counts::new, Counts::increment, Counts::combine)
21
+                .buildMap();
22
+    }
23
+
24
+    private static class Counts {
25
+
26
+        private Map<Integer, Integer> letterCounts = new HashMap<>();
27
+
28
+        private void increment(int letter) {
29
+            letterCounts.put(letter, letterCounts.getOrDefault(letter, 0) + 1);
30
+        }
31
+
32
+        private void combine(Counts other) {
33
+            other.letterCounts.keySet()
34
+                    .forEach(letter -> letterCounts.merge(letter, other.letterCounts.get(letter), Integer::sum));
35
+        }
36
+
37
+        private Map<Integer, Integer> buildMap() {
38
+            return Collections.unmodifiableMap(letterCounts);
39
+        }
40
+    }
41
+
42
+    Map<Integer, Integer> letterCounts() {
43
+        return letterFrequencyMap;
44
+    }
45
+}

+ 28
- 0
exercises/parallel-letter-frequency/README.md Просмотреть файл

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
+# Java Tips
11
+
12
+Single-threaded (non-concurrent) solutions can pass all tests [but the last.](https://www.youtube.com/watch?v=mJZZNHekEQw)  Your solution will be tested for concurrency by submitting it as a [Runnable](https://docs.oracle.com/javase/7/docs/api/java/lang/Runnable.html) to an [ExecutorService.](https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ExecutorService.html) Your solution must leverage multiple [Threads](https://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html) to pass the final test.
13
+
14
+Java documentation on [parallel streams](https://docs.oracle.com/javase/tutorial/collections/streams/parallelism.html) may provide some help.
15
+
16
+# Running the tests
17
+
18
+You can run all the tests for an exercise by entering
19
+
20
+```sh
21
+$ gradle test
22
+```
23
+
24
+in your terminal.
25
+
26
+## Submitting Incomplete Solutions
27
+
28
+It's possible to submit an incomplete solution so you can see how others have completed the exercise.

+ 18
- 0
exercises/parallel-letter-frequency/build.gradle Просмотреть файл

1
+apply plugin: "java"
2
+apply plugin: "eclipse"
3
+apply plugin: "idea"
4
+
5
+repositories {
6
+  mavenCentral()
7
+}
8
+
9
+dependencies {
10
+  testCompile "junit:junit:4.12"
11
+}
12
+
13
+test {
14
+  testLogging {
15
+    exceptionFormat = 'full'
16
+    events = ["passed", "failed", "skipped"]
17
+  }
18
+}

+ 0
- 0
exercises/parallel-letter-frequency/src/main/java/.keep Просмотреть файл


+ 235
- 0
exercises/parallel-letter-frequency/src/test/java/ParallelLetterFrequencyTest.java Просмотреть файл

1
+import org.junit.*;
2
+
3
+import static org.junit.Assert.*;
4
+
5
+import java.util.concurrent.*;
6
+import java.util.stream.*;
7
+import java.util.*;
8
+
9
+public class ParallelLetterFrequencyTest {
10
+
11
+    // Poem by Friedrich Schiller. The corresponding music is the European Anthem.
12
+    private String OdeAnDieFreude =
13
+            "Freude schöner Götterfunken\n" +
14
+                    "Tochter aus Elysium,\n" +
15
+                    "Wir betreten feuertrunken,\n" +
16
+                    "Himmlische, dein Heiligtum!\n" +
17
+                    "Deine Zauber binden wieder\n" +
18
+                    "Was die Mode streng geteilt;\n" +
19
+                    "Alle Menschen werden Brüder,\n" +
20
+                    "Wo dein sanfter Flügel weilt.";
21
+
22
+    // Dutch national anthem
23
+    private String Wilhelmus =
24
+            "Wilhelmus van Nassouwe\n" +
25
+                    "ben ik, van Duitsen bloed,\n" +
26
+                    "den vaderland getrouwe\n" +
27
+                    "blijf ik tot in den dood.\n" +
28
+                    "Een Prinse van Oranje\n" +
29
+                    "ben ik, vrij, onverveerd,\n" +
30
+                    "den Koning van Hispanje\n" +
31
+                    "heb ik altijd geëerd.";
32
+
33
+    // American national anthem
34
+    private String StarSpangledBanner =
35
+            "O say can you see by the dawn's early light,\n" +
36
+                    "What so proudly we hailed at the twilight's last gleaming,\n" +
37
+                    "Whose broad stripes and bright stars through the perilous fight,\n" +
38
+                    "O'er the ramparts we watched, were so gallantly streaming?\n" +
39
+                    "And the rockets' red glare, the bombs bursting in air,\n" +
40
+                    "Gave proof through the night that our flag was still there;\n" +
41
+                    "O say does that star-spangled banner yet wave,\n" +
42
+                    "O'er the land of the free and the home of the brave?\n";
43
+
44
+
45
+    @Test
46
+    public void noTextsMeansNoLetters() {
47
+        String input = "";
48
+        Map<Integer, Integer> expectedOutput = new HashMap<Integer, Integer>();
49
+        ParallelLetterFrequency p = new ParallelLetterFrequency(input);
50
+
51
+        assertEquals(expectedOutput, p.letterCounts());
52
+    }
53
+
54
+    @Ignore("Remove to run test")
55
+    @Test
56
+    public void oneLetterIsCorrectlyCounted() {
57
+        String input = "a";
58
+        Map<Integer, Integer> expectedOutput = new HashMap<Integer, Integer>() {
59
+            {
60
+                put((int) 'a', 1);
61
+            }
62
+        };
63
+        ParallelLetterFrequency p = new ParallelLetterFrequency(input);
64
+
65
+        assertEquals(expectedOutput, p.letterCounts());
66
+    }
67
+
68
+    @Ignore("Remove to run test")
69
+    @Test
70
+    public void resultsAreCaseInsensitive() {
71
+        String input = "Aa";
72
+        Map<Integer, Integer> expectedOutput = new HashMap<Integer, Integer>() {
73
+            {
74
+                put((int) 'a', 2);
75
+            }
76
+        };
77
+        ParallelLetterFrequency p = new ParallelLetterFrequency(input);
78
+
79
+        assertEquals(expectedOutput, p.letterCounts());
80
+    }
81
+
82
+
83
+    @Ignore("Remove to run test")
84
+    @Test
85
+    public void biggerEmptyTextsStillReturnNoResults() {
86
+        StringBuilder b = new StringBuilder();
87
+        for (int i = 0; i < 10000; i++) {
88
+            b.append(" ");
89
+        }
90
+
91
+        Map<Integer, Integer> expectedOutput = new HashMap<Integer, Integer>();
92
+        ParallelLetterFrequency p = new ParallelLetterFrequency(b.toString());
93
+
94
+        assertEquals(expectedOutput, p.letterCounts());
95
+    }
96
+
97
+    @Ignore("Remove to run test")
98
+    @Test
99
+    public void manyRepetitionsOfTheSameTextGiveAPredictableResult() {
100
+        StringBuilder b = new StringBuilder();
101
+        for (int i = 0; i < 10000; i++) {
102
+            b.append("abc");
103
+        }
104
+
105
+        Map<Integer, Integer> expectedOutput = new HashMap<Integer, Integer>() {
106
+            {
107
+                put((int) 'a', 10000);
108
+                put((int) 'b', 10000);
109
+                put((int) 'c', 10000);
110
+            }
111
+        };
112
+        ParallelLetterFrequency p = new ParallelLetterFrequency(b.toString());
113
+
114
+        assertEquals(expectedOutput, p.letterCounts());
115
+    }
116
+
117
+
118
+    @Ignore("Remove to run test")
119
+    @Test
120
+    public void punctuationDoesntCount() {
121
+        ParallelLetterFrequency p = new ParallelLetterFrequency(OdeAnDieFreude);
122
+
123
+        assertFalse(p.letterCounts().containsKey((int) ','));
124
+    }
125
+
126
+    @Ignore("Remove to run test")
127
+    @Test
128
+    public void numbersDontCount() {
129
+        ParallelLetterFrequency p = new ParallelLetterFrequency("Testing, 1, 2, 3");
130
+
131
+        assertFalse(p.letterCounts().containsKey((int) '1'));
132
+    }
133
+
134
+    @Ignore("Remove to run test")
135
+    @Test
136
+    public void allThreeAnthemsTogetherProduceCorrectCounts() {
137
+        StringBuilder b = new StringBuilder();
138
+        b.append(OdeAnDieFreude);
139
+        b.append(Wilhelmus);
140
+        b.append(StarSpangledBanner);
141
+
142
+        ParallelLetterFrequency p = new ParallelLetterFrequency(b.toString());
143
+
144
+        assertEquals(new Integer(49), p.letterCounts().get((int) 'a'));
145
+        assertEquals(new Integer(56), p.letterCounts().get((int) 't'));
146
+        assertEquals(new Integer(2), p.letterCounts().get((int) 'ü'));
147
+    }
148
+
149
+    @Ignore("Remove to run test")
150
+    @Test
151
+    public void multipleThreadsGetUsed()
152
+            throws InterruptedException, ExecutionException {
153
+
154
+
155
+        class MyForkJoinWorkerThread extends ForkJoinWorkerThread {
156
+
157
+            boolean wasStarted = false;
158
+
159
+            public MyForkJoinWorkerThread(ForkJoinPool pool) {
160
+                super(pool);
161
+            }
162
+
163
+            @Override
164
+            public void start() {
165
+                super.start();
166
+                this.wasStarted = true;
167
+            }
168
+        }
169
+
170
+        List<MyForkJoinWorkerThread> allThreads = new ArrayList<>();
171
+
172
+        class MyForkJoinThreadFactory implements ForkJoinPool.ForkJoinWorkerThreadFactory {
173
+            public MyForkJoinThreadFactory() {
174
+                super();
175
+            }
176
+
177
+            public MyForkJoinWorkerThread newThread(ForkJoinPool pool) {
178
+                final MyForkJoinWorkerThread worker = new MyForkJoinWorkerThread(pool);
179
+                worker.setName("Thread number: " + worker.getPoolIndex());
180
+                allThreads.add(worker);
181
+                return worker;
182
+            }
183
+        }
184
+
185
+        MyForkJoinThreadFactory factory = new MyForkJoinThreadFactory();
186
+        ForkJoinPool multiThreadPool = new ForkJoinPool(4, (ForkJoinPool.ForkJoinWorkerThreadFactory) factory, null, false);
187
+
188
+        String input = "abcdefghijklmnopqrstuvwxyz";
189
+        StringBuilder b = new StringBuilder();
190
+        for (int i = 1; i < 10000000; i++) {
191
+            b.append(input);
192
+        }
193
+
194
+        Map<Integer, Integer> expectedOutput = new HashMap<Integer, Integer>() {
195
+            {
196
+                put((int) 'a', 10000000);
197
+                put((int) 'b', 10000000);
198
+                put((int) 'c', 10000000);
199
+                put((int) 'd', 10000000);
200
+                put((int) 'e', 10000000);
201
+                put((int) 'f', 10000000);
202
+                put((int) 'g', 10000000);
203
+                put((int) 'h', 10000000);
204
+                put((int) 'i', 10000000);
205
+                put((int) 'j', 10000000);
206
+                put((int) 'k', 10000000);
207
+                put((int) 'l', 10000000);
208
+                put((int) 'm', 10000000);
209
+                put((int) 'n', 10000000);
210
+                put((int) 'o', 10000000);
211
+                put((int) 'p', 10000000);
212
+                put((int) 'q', 10000000);
213
+                put((int) 'r', 10000000);
214
+                put((int) 's', 10000000);
215
+                put((int) 't', 10000000);
216
+                put((int) 'u', 10000000);
217
+                put((int) 'v', 10000000);
218
+                put((int) 'w', 10000000);
219
+                put((int) 'x', 10000000);
220
+                put((int) 'y', 10000000);
221
+                put((int) 'z', 10000000);
222
+            }
223
+        };
224
+
225
+        Map<Integer, Integer> multiCounts = multiThreadPool.submit(
226
+                () -> (new ParallelLetterFrequency(b.toString()).letterCounts())
227
+        ).get();
228
+
229
+        boolean startedThreadCountEqualsPoolSize = allThreads.stream()
230
+                .filter(thread -> thread.wasStarted)
231
+                .count() == allThreads.size();
232
+
233
+        assertTrue(startedThreadCountEqualsPoolSize);
234
+    }
235
+}

+ 1
- 0
exercises/settings.gradle Просмотреть файл

49
 include 'octal'
49
 include 'octal'
50
 include 'palindrome-products'
50
 include 'palindrome-products'
51
 include 'pangram'
51
 include 'pangram'
52
+include 'parallel-letter-frequency'
52
 include 'pascals-triangle'
53
 include 'pascals-triangle'
53
 include 'perfect-numbers'
54
 include 'perfect-numbers'
54
 include 'phone-number'
55
 include 'phone-number'