Pattern Puzzlers 2024 Edition

.jpg

https://horstmann.com/presentations/2024/jcrete

Slide navigation: Forward with space bar, , or swipe left. Backwards with or swipe right. Switch slides with PgDn and PgUp.

Why Switch?

Why Pattern Matching?

Puzzler #1: Colons, Arrows, Braces, Break, Yield

Consider these case clauses:

case 5 -> "Friday"; // 1
case 5 -> "Friday".toUpperCase(); // 2
case 5 -> yield "Friday"; // 3
case 5 -> { break; } // 4
case 5 -> if (Math.random() < 0.5) System.out.println("Friday"); // 5
case 5 -> throw new Exception("Friday"); // 6

How many of these can be valid in a switch statement?

  1. 0
  2. 1
  3. 2
  4. 3
  5. 4 or more

Puzzler #2: Selector Types

Which of the following are legal switch expressions?

Object x = ...;
String result = switch (x) { // I
   case "" -> "empty";
   case 0 -> "zero";
   default -> "something else";
};

enum Size { SMALL, MEDIUM, LARGE, EXTRA_LARGE };
Object y = ...;
result = switch (y) { // II
   case Size.EXTRA_LARGE -> "extra large";
   default -> "something else";
};
  1. Neither is legal
  2. Only I is legal
  3. Only II is legal
  4. Both I and II are legal

Puzzler #3: Null

How many of these switch expressions throw a NullPointerException?

record Box<T>(T contents) { }

Box<String> boxed = null;
String unboxed = switch (boxed) {
   case Box(String s) -> s;
};

boxed = new Box<>(null);
String unboxed = switch (boxed) {
   case Box(String s) -> s;
};

Box<Box<String>> doubleBoxed = new Box<>(null);
String unboxed = switch (doubleBoxed) {
   case Box(Box(String s)) -> s;
};
  1. None
  2. One
  3. Two
  4. All three

Puzzler #4: Dominance

Which of these switch expressions compile?

Object x = ...;
String d = switch (x) { // I
   case Number n -> "a number";
   case Integer i -> "an integer";
   default -> "something else";
};

Integer y = ...;
String e = switch (y) { // II
   case Integer i when i > 0 -> "positive";
   default -> "negative";
   case 0 -> "zero";
};
  1. Neither
  2. Only I
  3. Only II
  4. Both

Puzzler #5: Primitive Type Patterns

void main() {
   int count = 0;
   float sum = 0;
   for (int i = 1; i <= 100; i++) {
      if (1.0 / i instanceof float f) {
         count++;
         sum += f;
      }
   }
   System.out.println(count + " " + sum);
}

What does the program print?

  1. 100 5050.0
  2. 0 0
  3. 7 1.984375
  4. The program doesn't compile

Puzzler #6: Exceptions

Integer foo(int x) {
   int y = switch (x) {
      case 0 -> throw new NullPointerException();
      case 1 -> throw new IllegalArgumentException();
      default -> throw new IndexOutOfBoundsException();
   };
   return y;
}

int main() {
   System.out.println(
      switch (foo(3)) {
         case 0 -> "zero";
         case null -> "null";
         default -> "something else";
         case throws RuntimeException -> "oh noes";
      });
}

What will happen?

  1. foo fails to compile
  2. main fails to compile
  3. "oh noes" is printed
  4. The program terminates with an exception

Puzzler #7: Deconstruction

Suppose the j.u.r.Pattern class is enhanced with this deconstructor:

public inverse String match(String... groups) {
    Matcher m = matcher(that);    // *that* is the match candidate
    if (m.matches())              // receiver for matcher() is the Pattern
        match(IntStream.range(1, m.groupCount())
                            .map(m::group)
                            .toArray(String[]::new));
}

What is the result of

switch("0:59") {
   case Pattern.of("([0-2][0-9]):([0-9]+)").match(h, m) -> 60 * Integer.parseInt(h) + Integer.parseInt(m);
   default -> -1;
}
  1. 59
  2. -1
  3. a MatchError is thrown
  4. A compiler error that groups is not used

Enough Already—What's the TL;DR?

.jpg