-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathINSTRUCTIONS.txt
220 lines (196 loc) · 8.33 KB
/
INSTRUCTIONS.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
// @BEGIN_VERSION_ONLY HELLO_GENERICS
Hello Generics
--------------
The HelloTest class doesn't compile. Create a Hello class in src/main/java
(not in src/test/java...).
Write a static method that repeats its input n times.
// @END_VERSION_ONLY HELLO_GENERICS
// @BEGIN_VERSION_ONLY HELLO_GENERICS2
Hello Generics2
---------------
Make your static method work for any type. Hello, Generics!
// @END_VERSION_ONLY HELLO_GENERICS2
// @BEGIN_VERSION_ONLY HELLO_FUNCTION
Hello Unary Function
--------------
Write a Function that squares an Integer.
// @END_VERSION_ONLY HELLO_FUNCTION
// @BEGIN_VERSION_ONLY HELLO_BINARY_FUNCTION
Hello Binary Function
---------------------
Create a Function2 interface similar to com.google.common.base.Function.
Be careful with the generic types for the apply method!
Write a Function2 that adds two Integers.
// @END_VERSION_ONLY HELLO_BINARY_FUNCTION
// @BEGIN_VERSION_ONLY TO_BACKGROUND_FUNCTION
To Background Function
----------------------
The first step towards a parallel transform is to turn a function into
another function that is executed in the background.
Given a Function<F, T> we need to turn this into a Function<F, Future<T>>
public static <F,T> Function<F, Future<T>> toBackgroundFunction(final Function<F, T> f) {
...
}
Details:
We're building a parallel version of transform. The idea is this: transform
already applies a Function f to all elements in a collection. So if we can
present a Function that takes f and executes it on a background thread, then
we can simply call transform with that new function. The new function will be
applied to each element in the collection, and execute f on the element but
do it in the background.
// @END_VERSION_ONLY TO_BACKGROUND_FUNCTION
// @BEGIN_VERSION_ONLY BACKGROUND_TRANSFORM
Background Transform
--------------------
OK, so the previous step produced a method that gives us a function that does
whatever f does, but in the background.
Apply the background function you just implemented using Collections2.transform.
Base the static method signature on Collections2.transform.
The second step towards a parallel transform is to execute transform with a
collection and your new background function. However, you should consider the
consequences of laziness.
// @END_VERSION_ONLY BACKGROUND_TRANSFORM
// @BEGIN_VERSION_ONLY FROM_FUTURE_FUNCTION
From Future Function
--------------------
The previous step actually runs Function f in the background for all elements
in the collection. Now you need to get the result of the background calculation.
Start by writing a Function that turns a Future<A> back into an A.
// @END_VERSION_ONLY FROM_FUTURE_FUNCTION
// @BEGIN_VERSION_ONLY GET_ALL
Get All
-------
Apply the fromFuture function that you wrote in the previous step to a
collection of Futures.
public static <A> Collection<A> getAll(Collection<Future<A>> coll) {
...
}
Use Collections2.transform. Note that Java's type inference is pretty crappy,
so you need to write something like Collections3.<A>fromFuture().
// @END_VERSION_ONLY GET_ALL
// @BEGIN_VERSION_ONLY REGULAR_TRANSFORM
Regular Transform
-----------------
This step has a test that illustrates the problem with regular transform with a
function that takes a significant amount of time. Just run the test and watch
it fail. Ponder for a moment what can be done about it, but then move on to the
next step.
mvn lab:next
// @END_VERSION_ONLY REGULAR_TRANSFORM
// @BEGIN_VERSION_ONLY PARALLEL_TRANSFORM
Parallel Transform
------------------
In this step, the test has been modified to use a parallel version of transform.
Stitch together the two larger building blocks you have produced so far. Make
sure you get generic types right.
// @END_VERSION_ONLY PARALLEL_TRANSFORM
// @BEGIN_VERSION_ONLY REDUCE
Reduce
------
Google Guava has map and filter, but no reduce. In this step, you will
implement reduce.
Reduce takes a collection coll, and a function f. f is a function of 2
arguments. Reduce returns the result of applying f to the first item in coll
and the 2nd item, then applying f to that result and the 3rd item, etc.
Here is a skeleton for reduce:
public class Collections3 {
public static <A> A reduce(Iterable<A> coll, Function2<A, A, A> f) {
...
}
}
Here is an example of using reduce:
Function2<Integer, Integer, Integer> plus = new Function2<Integer, Integer, Integer>() {
public Integer apply(Integer accum, Integer next) {
return accum + next;
}
};
Collections3.reduce(Arrays.asList(1, 2, 3, 4), plus);
// @END_VERSION_ONLY REDUCE
// @BEGIN_VERSION_ONLY FOLD
Fold
----
Google Guava doesn't have a fold either. In this step, you will implement
fold.
Fold takes an initial value, but reduce doesn't. Fold takes a collection coll,
an initial value val, and a function f. f is a function of 2 arguments. Fold
returns the result of applying f to val and the first item in coll, then
applying f to that result and the 2nd item, etc.
Here is a skeleton for fold:
public class Collections3 {
public static <A,B> A fold(Iterable<B> coll, A val, Function2<A, B, A> f) {
...
}
}
Note that fold takes a Function2, just as reduce did, although the 'next' argument
may be of a different type than 'accum'. Here is an example of using fold:
Function2<Integer, Integer, Integer> times = new Function2<Integer, Integer, Integer>() {
public Integer apply(Integer accum, Integer next) {
return accum * next;
}
};
Collections3.fold(Arrays.asList(1, 2, 3, 4), 2, times);
Note the initial value 2.
// @END_VERSION_ONLY FOLD
// @BEGIN_VERSION_ONLY GATHER_WORDS_WHITESPACE
Gather Words, Whitespace
------------------------
Back to the real business problem. In the test method comments, you can see
the Clojure equivalents of the tests. I have added them there because I think
that they convey the essence of the tests quite clearly.
Write a method that extracts words from a string, splitting words on
whitespace. Note that there is a main method that you can run on any text file
of choice, like for example the file mary.txt in the project root.
// @END_VERSION_ONLY GATHER_WORDS_WHITESPACE
// @BEGIN_VERSION_ONLY GATHER_WORDS_PUNCTUATION
Gather Words, Remove Punctuation
--------------------------------
Remove not only whitespace, but also any punctuation. Run main and watch the
output.
// @END_VERSION_ONLY GATHER_WORDS_PUNCTUATION
// @BEGIN_VERSION_ONLY GATHER_WORDS_LOWERCASE
Gather Words, Ignore Case
-------------------------
Ignore the case of the words by converting them to lowercase. Run main.
// @END_VERSION_ONLY GATHER_WORDS_LOWERCASE
// @BEGIN_VERSION_ONLY COUNT_WORDS
Count Words
-----------
Count the words into a map from words to count. Perhaps you'll get some use of
that fold implementation now?
// @END_VERSION_ONLY COUNT_WORDS
// @BEGIN_VERSION_ONLY SORT_COUNTED_WORDS
Sort Counted Words
------------------
Sort the map by count and return a list of word/count pairs. What's the simplest
implementation of a pair? I don't know, but we chose to wrap the pair in a class
WordCountPair, so that's what you'll use.
// @END_VERSION_ONLY SORT_COUNTED_WORDS
// @BEGIN_VERSION_ONLY REPEAT_STR
Repeat String
-------------
Repeat a string a given number of times. Like 'repeatStr "#" 3' becomes "###".
// @END_VERSION_ONLY REPEAT_STR
// @BEGIN_VERSION_ONLY HISTOGRAM_ENTRY
Histogram Entry
---------------
Produce a single entry in the histogram. Calling histogramEntry with a word-count
pair of ["betty" 6] and a width of 7 should return the word "betty" left-justified
within a field of 7 characters, a blank, and then six hashes: "betty ######".
// @END_VERSION_ONLY HISTOGRAM_ENTRY
// @BEGIN_VERSION_ONLY HISTOGRAM
Histogram
---------
Produce a complete histogram. It's just a bunch of histogram entries.
// @END_VERSION_ONLY HISTOGRAM
// @BEGIN_VERSION_ONLY DESIGN_QUESTIONS
Design Questions
----------------
The lab is now complete, but here are some questions that you should consider:
* Why are we using a static function rather than a field to create the function
fromFuture? Could it be done differently? Pros and cons?
* Is Collections3 a good place to store all the functions? Other suggestions?
(Compare with Google Guava)
* Both transformInBackground and fromFuture are stateful, but are used as
static methods. Can you come up with a different design? There are at least
two completely different approaches.
// @END_VERSION_ONLY DESIGN_QUESTIONS