From f4d6256f5a42a554fe7ad539d96c05c3098fab56 Mon Sep 17 00:00:00 2001 From: Stuart Marks <stuart.marks@oracle.com> Date: Tue, 30 Aug 2016 11:18:14 -0700 Subject: [PATCH] Editing on hints. Rewrite g4_splitCharacterRuns(). --- LambdaLab/test/exercises/D_SimpleStreams.java | 10 +++- .../test/exercises/E_IntermediateStreams.java | 16 ++++-- .../test/exercises/F_AdvancedStreams.java | 12 +++- LambdaLab/test/exercises/G_Challenges.java | 41 +++++++++++++- LambdaLab/test/solutions/D_SimpleStreams.java | 10 +++- .../test/solutions/E_IntermediateStreams.java | 22 +++++--- .../test/solutions/F_AdvancedStreams.java | 12 +++- LambdaLab/test/solutions/G_Challenges.java | 56 +++++++++++++++---- 8 files changed, 142 insertions(+), 37 deletions(-) diff --git a/LambdaLab/test/exercises/D_SimpleStreams.java b/LambdaLab/test/exercises/D_SimpleStreams.java index 6e2e31b..23e27ea 100644 --- a/LambdaLab/test/exercises/D_SimpleStreams.java +++ b/LambdaLab/test/exercises/D_SimpleStreams.java @@ -47,7 +47,8 @@ public class D_SimpleStreams { // </editor-fold> // Hint 2: // <editor-fold defaultstate="collapsed"> - // Use collect() to create the result list. + // To create the result list, use collect() with one of the + // predefined collectors on the Collectors class. // </editor-fold> @@ -111,12 +112,17 @@ public class D_SimpleStreams { } // Hint 1: // <editor-fold defaultstate="collapsed"> - // Use Stream.mapToInt() to convert to IntStream. + // Use Stream.mapToInt() to convert a stream of objects to an IntStream. // </editor-fold> // Hint 2: // <editor-fold defaultstate="collapsed"> // Look at java.util.OptionalInt to get the result. // </editor-fold> + // Hint 3: + // <editor-fold defaultstate="collapsed"> + // Think about the case where the OptionalInt might be empty + // (that is, where it has no value). + // </editor-fold> /** diff --git a/LambdaLab/test/exercises/E_IntermediateStreams.java b/LambdaLab/test/exercises/E_IntermediateStreams.java index 80062e7..55bff1f 100644 --- a/LambdaLab/test/exercises/E_IntermediateStreams.java +++ b/LambdaLab/test/exercises/E_IntermediateStreams.java @@ -11,7 +11,7 @@ import java.util.Comparator; import java.util.List; import java.util.regex.Pattern; import java.util.stream.Collectors; -import java.util.stream.IntStream; +import java.util.stream.LongStream; import org.junit.After; import org.junit.Before; @@ -52,7 +52,7 @@ public class E_IntermediateStreams { /** * Collect all the words from the text file into a list. - * Use the regular expression pattern WORD_PATTERN to split + * Use the regular expression Pattern WORD_PATTERN to split * a string into words, and use Pattern.splitAsStream(String) * to do the splitting. WORD_PATTERN is defined at the bottom * of this file. @@ -178,9 +178,15 @@ public class E_IntermediateStreams { assertEquals(new BigInteger("51090942171709440000"), result); } - // Hint: + // Hint 1: + // <editor-fold defaultstate="collapsed"> + // Use one of the range methods of LongStream to help create + // the BigInteger instances. + // </editor-fold> + // Hint 2: // <editor-fold defaultstate="collapsed"> - // Use LongStream and reduction. + // Use Stream.reduce() to "collapse" all elements of a stream into + // a single value. // </editor-fold> @@ -197,7 +203,7 @@ public class E_IntermediateStreams { } // Hint: // <editor-fold defaultstate="collapsed"> - // Use Stream.reduce(). + // Use Stream.reduce() and think about the order of the arguments. // </editor-fold> diff --git a/LambdaLab/test/exercises/F_AdvancedStreams.java b/LambdaLab/test/exercises/F_AdvancedStreams.java index 6327f29..b359a2a 100644 --- a/LambdaLab/test/exercises/F_AdvancedStreams.java +++ b/LambdaLab/test/exercises/F_AdvancedStreams.java @@ -91,7 +91,8 @@ public class F_AdvancedStreams { } // Hint 1: // <editor-fold defaultstate="collapsed"> - // Use the "downstream" overload of Collectors.groupingBy(). + // Use the overload of Collectors.groupingBy() that has + // a "downstream" parameter. // </editor-fold> // Hint 2: // <editor-fold defaultstate="collapsed"> @@ -162,8 +163,8 @@ public class F_AdvancedStreams { } // Hint 1: // <editor-fold defaultstate="collapsed"> - // The nested map structure that's the desired is the result of applying - // a "downstream" collector that's the same operation as the first-level collector. + // The nested map structure that's desired is the result of applying a + // "downstream" collector that's the same operation as the first-level collector. // </editor-fold> // Hint 2: // <editor-fold defaultstate="collapsed"> @@ -263,6 +264,11 @@ public class F_AdvancedStreams { int getTotalCount() { return count; } int getDistinctCount() { return set.size(); } } + // Hint: + // <editor-fold defaultstate="collapsed"> + // The operations you need to write are actually quite simple. + // Don't overthink it. + // </editor-fold> @Test @Ignore public void f7_countTotalAndDistinctWords() { diff --git a/LambdaLab/test/exercises/G_Challenges.java b/LambdaLab/test/exercises/G_Challenges.java index 58d8d9f..e1738ba 100644 --- a/LambdaLab/test/exercises/G_Challenges.java +++ b/LambdaLab/test/exercises/G_Challenges.java @@ -109,6 +109,10 @@ public class G_Challenges { * in the value sets for both p and q in the input map. Therefore, * in the result map, there should be a mapping with 20 as the key * and p and q as its value set. + * + * It is possible to accomplish this task using a single stream + * pipeline (not counting nested streams), that is, in a single pass + * over the input, without storing anything in a temporary collection. */ @Test @Ignore public void g2_invertMultiMap() { @@ -128,6 +132,17 @@ public class G_Challenges { assertEquals(new HashSet<>(Arrays.asList("d", "e", "f")), result.get(4)); assertEquals(4, result.size()); } + // Hint 1: + // <editor-fold defaultstate="collapsed"> + // A general approach is to flatten the input structure in one stage + // of the pipeline and then to create the result structure using a collector. + // </editor-fold> + // Hint 2: + // <editor-fold defaultstate="collapsed"> + // A useful intermediate data structure after the flattening step + // is a pair of items. You can write your own pair class, or you can + // use a pre-existing class like AbstractMap.SimpleEntry. + // </editor-fold> /** @@ -158,7 +173,6 @@ public class G_Challenges { - /** * Given a string, split it into a list of strings consisting of * consecutive characters from the original string. Note: this is @@ -173,6 +187,12 @@ public class G_Challenges { assertEquals("[aaaaa, bb, cccc, d, eeeeee, aaa, fff]", result.toString()); } + // Hint: + // <editor-fold defaultstate="collapsed"> + // One possibility is a two-pass approach: one pass to gather data about + // the boundaries between the runs, and the second to create the substrings + // based on output from the first. + // </editor-fold> /** * Given a parallel stream of strings, collect them into a collection in reverse order. @@ -188,7 +208,6 @@ public class G_Challenges { input.collect(Collector.of(null, null, null)); // TODO fill in collector functions above - assertEquals( IntStream.range(0, 100) .map(i -> 99 - i) @@ -196,6 +215,14 @@ public class G_Challenges { .collect(Collectors.toList()), new ArrayList<>(result)); } + // Hint 1: + // <editor-fold defaultstate="collapsed"> + // ArrayDeque supports fast insertion at the front. + // </editor-fold> + // Hint 2: + // <editor-fold defaultstate="collapsed"> + // Be careful with ordering of the arguments and results in the combiner. + // </editor-fold> /** * Given an array of int, find the int value that occurs a majority @@ -212,6 +239,11 @@ public class G_Challenges { OptionalInt majority(int[] array) { return null; // TODO } + // Hint: + // <editor-fold defaultstate="collapsed"> + // A two-pass approach may be called for here: a counting pass + // and a majority-finding pass. + // </editor-fold> @Test @Ignore public void g6_majority() { @@ -238,6 +270,11 @@ public class G_Challenges { Supplier<Shoe> makeShoeSupplier(IntFunction<Shoe> ifunc, int size) { return null; // TODO } + // Hint: + // <editor-fold defaultstate="collapsed"> + // You don't want to return the result of calling the IntFunction. + // Instead, you want to return a lambda that calls the IntFunction. + // </editor-fold> static class Shoe { final int size; diff --git a/LambdaLab/test/solutions/D_SimpleStreams.java b/LambdaLab/test/solutions/D_SimpleStreams.java index bdd2198..f31048d 100644 --- a/LambdaLab/test/solutions/D_SimpleStreams.java +++ b/LambdaLab/test/solutions/D_SimpleStreams.java @@ -57,7 +57,8 @@ public class D_SimpleStreams { // </editor-fold> // Hint 2: // <editor-fold defaultstate="collapsed"> - // Use collect() to create the result list. + // To create the result list, use collect() with one of the + // predefined collectors on the Collectors class. // </editor-fold> @@ -140,12 +141,17 @@ public class D_SimpleStreams { } // Hint 1: // <editor-fold defaultstate="collapsed"> - // Use Stream.mapToInt() to convert to IntStream. + // Use Stream.mapToInt() to convert a stream of objects to an IntStream. // </editor-fold> // Hint 2: // <editor-fold defaultstate="collapsed"> // Look at java.util.OptionalInt to get the result. // </editor-fold> + // Hint 3: + // <editor-fold defaultstate="collapsed"> + // Think about the case where the OptionalInt might be empty + // (that is, where it has no value). + // </editor-fold> /** diff --git a/LambdaLab/test/solutions/E_IntermediateStreams.java b/LambdaLab/test/solutions/E_IntermediateStreams.java index 1f2717a..3f478d5 100644 --- a/LambdaLab/test/solutions/E_IntermediateStreams.java +++ b/LambdaLab/test/solutions/E_IntermediateStreams.java @@ -11,7 +11,7 @@ import java.util.Comparator; import java.util.List; import java.util.regex.Pattern; import java.util.stream.Collectors; -import java.util.stream.IntStream; +import java.util.stream.LongStream; import org.junit.After; import org.junit.Before; @@ -58,7 +58,7 @@ public class E_IntermediateStreams { /** * Collect all the words from the text file into a list. - * Use the regular expression pattern WORD_PATTERN to split + * Use the regular expression Pattern WORD_PATTERN to split * a string into words, and use Pattern.splitAsStream(String) * to do the splitting. WORD_PATTERN is defined at the bottom * of this file. @@ -218,16 +218,22 @@ public class E_IntermediateStreams { //TODO//BigInteger result = BigInteger.ONE; //BEGINREMOVE BigInteger result = - IntStream.rangeClosed(1, 21) - .mapToObj(n -> BigInteger.valueOf(n)) - .reduce(BigInteger.ONE, (m, n) -> m.multiply(n)); + LongStream.rangeClosed(1, 21) + .mapToObj(BigInteger::valueOf) + .reduce(BigInteger.ONE, BigInteger::multiply); //ENDREMOVE assertEquals(new BigInteger("51090942171709440000"), result); } - // Hint: + // Hint 1: + // <editor-fold defaultstate="collapsed"> + // Use one of the range methods of LongStream to help create + // the BigInteger instances. + // </editor-fold> + // Hint 2: // <editor-fold defaultstate="collapsed"> - // Use LongStream and reduction. + // Use Stream.reduce() to "collapse" all elements of a stream into + // a single value. // </editor-fold> @@ -251,7 +257,7 @@ public class E_IntermediateStreams { } // Hint: // <editor-fold defaultstate="collapsed"> - // Use Stream.reduce(). + // Use Stream.reduce() and think about the order of the arguments. // </editor-fold> diff --git a/LambdaLab/test/solutions/F_AdvancedStreams.java b/LambdaLab/test/solutions/F_AdvancedStreams.java index f5c15e2..2fcb974 100644 --- a/LambdaLab/test/solutions/F_AdvancedStreams.java +++ b/LambdaLab/test/solutions/F_AdvancedStreams.java @@ -103,7 +103,8 @@ public class F_AdvancedStreams { } // Hint 1: // <editor-fold defaultstate="collapsed"> - // Use the "downstream" overload of Collectors.groupingBy(). + // Use the overload of Collectors.groupingBy() that has + // a "downstream" parameter. // </editor-fold> // Hint 2: // <editor-fold defaultstate="collapsed"> @@ -197,8 +198,8 @@ public class F_AdvancedStreams { } // Hint 1: // <editor-fold defaultstate="collapsed"> - // The nested map structure that's the desired is the result of applying - // a "downstream" collector that's the same operation as the first-level collector. + // The nested map structure that's desired is the result of applying a + // "downstream" collector that's the same operation as the first-level collector. // </editor-fold> // Hint 2: // <editor-fold defaultstate="collapsed"> @@ -326,6 +327,11 @@ public class F_AdvancedStreams { int getTotalCount() { return count; } int getDistinctCount() { return set.size(); } } + // Hint: + // <editor-fold defaultstate="collapsed"> + // The operations you need to write are actually quite simple. + // Don't overthink it. + // </editor-fold> @Test public void f7_countTotalAndDistinctWords() { diff --git a/LambdaLab/test/solutions/G_Challenges.java b/LambdaLab/test/solutions/G_Challenges.java index cd8ce39..ec6723f 100644 --- a/LambdaLab/test/solutions/G_Challenges.java +++ b/LambdaLab/test/solutions/G_Challenges.java @@ -131,6 +131,10 @@ public class G_Challenges { * in the value sets for both p and q in the input map. Therefore, * in the result map, there should be a mapping with 20 as the key * and p and q as its value set. + * + * It is possible to accomplish this task using a single stream + * pipeline (not counting nested streams), that is, in a single pass + * over the input, without storing anything in a temporary collection. */ @Test public void g2_invertMultiMap() { @@ -171,6 +175,17 @@ public class G_Challenges { assertEquals(new HashSet<>(Arrays.asList("d", "e", "f")), result.get(4)); assertEquals(4, result.size()); } + // Hint 1: + // <editor-fold defaultstate="collapsed"> + // A general approach is to flatten the input structure in one stage + // of the pipeline and then to create the result structure using a collector. + // </editor-fold> + // Hint 2: + // <editor-fold defaultstate="collapsed"> + // A useful intermediate data structure after the flattening step + // is a pair of items. You can write your own pair class, or you can + // use a pre-existing class like AbstractMap.SimpleEntry. + // </editor-fold> /** @@ -216,7 +231,7 @@ public class G_Challenges { len = slen; list.clear(); list.add(s); - } // ignore if slen < len + } // ignore input string if slen < len } Longest comb(Longest other) { @@ -237,7 +252,6 @@ public class G_Challenges { //ENDREMOVE - /** * Given a string, split it into a list of strings consisting of * consecutive characters from the original string. Note: this is @@ -250,23 +264,26 @@ public class G_Challenges { //TODO//List<String> result = null; //BEGINREMOVE - - List<Integer> bounds = + int[] bounds = IntStream.rangeClosed(0, input.length()) .filter(i -> i == 0 || i == input.length() || input.charAt(i-1) != input.charAt(i)) - .boxed() - .collect(Collectors.toList()); + .toArray(); List<String> result = - IntStream.range(1, bounds.size()) - .mapToObj(i -> input.substring(bounds.get(i-1), bounds.get(i))) + IntStream.range(1, bounds.length) + .mapToObj(i -> input.substring(bounds[i-1], bounds[i])) .collect(Collectors.toList()); - //ENDREMOVE assertEquals("[aaaaa, bb, cccc, d, eeeeee, aaa, fff]", result.toString()); } + // Hint: + // <editor-fold defaultstate="collapsed"> + // One possibility is a two-pass approach: one pass to gather data about + // the boundaries between the runs, and the second to create the substrings + // based on output from the first. + // </editor-fold> /** * Given a parallel stream of strings, collect them into a collection in reverse order. @@ -281,14 +298,11 @@ public class G_Challenges { //UNCOMMENT//Collection<String> result = //UNCOMMENT// input.collect(Collector.of(null, null, null)); //UNCOMMENT// // TODO fill in collector functions above - //BEGINREMOVE - Collection<String> result = input.collect(Collector.of(ArrayDeque::new, ArrayDeque::addFirst, (d1, d2) -> { d2.addAll(d1); return d2; })); - //ENDREMOVE assertEquals( @@ -298,6 +312,14 @@ public class G_Challenges { .collect(Collectors.toList()), new ArrayList<>(result)); } + // Hint 1: + // <editor-fold defaultstate="collapsed"> + // ArrayDeque supports fast insertion at the front. + // </editor-fold> + // Hint 2: + // <editor-fold defaultstate="collapsed"> + // Be careful with ordering of the arguments and results in the combiner. + // </editor-fold> /** * Given an array of int, find the int value that occurs a majority @@ -326,6 +348,11 @@ public class G_Challenges { .findAny(); //ENDREMOVE } + // Hint: + // <editor-fold defaultstate="collapsed"> + // A two-pass approach may be called for here: a counting pass + // and a majority-finding pass. + // </editor-fold> @Test public void g6_majority() { @@ -355,6 +382,11 @@ public class G_Challenges { return () -> ifunc.apply(size); //ENDREMOVE } + // Hint: + // <editor-fold defaultstate="collapsed"> + // You don't want to return the result of calling the IntFunction. + // Instead, you want to return a lambda that calls the IntFunction. + // </editor-fold> static class Shoe { final int size; -- GitLab