Pattern Matching
Pattern Matching
模式匹配是函数式编程语言中的概念,目前
@Test
public void whenIfWorksAsMatcher_thenCorrect() {
int input = 3;
String output;
if (input == 0) {
output = "zero";
}
if (input == 1) {
output = "one";
}
if (input == 2) {
output = "two";
}
if (input == 3) {
output = "three";
} else {
output = "unknown";
}
assertEquals("three", output);
}
@Test
public void whenSwitchWorksAsMatcher_thenCorrect() {
int input = 2;
String output;
switch (input) {
case 0:
output = "zero";
break;
case 1:
output = "one";
break;
case 2:
output = "two";
break;
case 3:
output = "three";
break;
default:
output = "unknown";
break;
}
assertEquals("two", output);
}
@Test
public void whenMatchworks_thenCorrect() {
int input = 2;
String output = Match(input).of(
Case($(1), "one"),
Case($(2), "two"),
Case($(3), "three"),
Case($(), "?"));
assertEquals("two", output);
}
原子模式代表了应该被评估以返回一个布尔值的条件。
- $():一个通配符模式,类似于
switch 语句中的默认情况。它处理没有找到匹配的情况。 $(value): 这是一种等值模式,将一个值与输入进行简单的等值比较。$(predicate): 这是一种条件模式,在这种模式下,一个谓词函数被应用到输入上,然后用结果的布尔值来做决定。
Option
正如我们在上一节中所看到的,通配符模式
@Test
public void whenMatchWorksWithOption_thenCorrect() {
int i = 10;
Option<String> s = Match(i)
.option(Case($(0), "zero"));
assertTrue(s.isEmpty());
assertEquals("None", s.toString());
}
Inbuilt Predicates
@Test
public void whenMatchWorksWithPredicate_thenCorrect() {
int i = 3;
String s = Match(i).of(
Case($(is(1)), "one"),
Case($(is(2)), "two"),
Case($(is(3)), "three"),
Case($(), "?"));
assertEquals("three", s);
}
@Test
public void givenInput_whenMatchesClass_thenCorrect() {
Object obj=5;
String s = Match(obj).of(
Case($(instanceOf(String.class)), "string matched"),
Case($(), "not string"));
assertEquals("not string", s);
}
或输入是否为空。
@Test
public void givenInput_whenMatchesNull_thenCorrect() {
Object obj=5;
String s = Match(obj).of(
Case($(isNull()), "no value"),
Case($(isNotNull()), "value found"));
assertEquals("value found", s);
}
我们可以使用
@Test
public void givenInput_whenContainsWorks_thenCorrect() {
int i = 5;
String s = Match(i).of(
Case($(isIn(2, 4, 6, 8)), "Even Single Digit"),
Case($(isIn(1, 3, 5, 7, 9)), "Odd Single Digit"),
Case($(), "Out of range"));
assertEquals("Odd Single Digit", s);
}
谓词组合
我们还可以用谓词来做更多的事情,比如将多个谓词组合成一个单一的匹配情况
@Test
public void givenInput_whenMatchAllWorks_thenCorrect() {
Integer i = null;
String s = Match(i).of(
Case($(allOf(isNotNull(),isIn(1,2,3,null))), "Number found"),
Case($(), "Not found"));
assertEquals("Not found", s);
}
为了在输入符合给定组的任何一个时进行匹配,我们可以使用
@Test
public void givenInput_whenMatchesAnyOfWorks_thenCorrect() {
Integer year = 1990;
String s = Match(year).of(
Case($(anyOf(isIn(1990, 1991, 1992), is(1986))), "Age match"),
Case($(), "No age match"));
assertEquals("Age match", s);
}
最后,我们可以使用
@Test
public void givenInput_whenMatchesNoneOfWorks_thenCorrect() {
Integer year = 1990;
String s = Match(year).of(
Case($(noneOf(isIn(1990, 1991, 1992), is(1986))), "Age match"),
Case($(), "No age match"));
assertEquals("No age match", s);
}
自定义谓词
@Test
public void whenMatchWorksWithCustomPredicate_thenCorrect() {
int i = 3;
String s = Match(i).of(
Case($(n -> n == 1), "one"),
Case($(n -> n == 2), "two"),
Case($(n -> n == 3), "three"),
Case($(), "?"));
assertEquals("three", s);
}
如果我们需要更多的参数,我们也可以应用一个函数接口来代替谓词。包含的例子可以这样重写,尽管比较啰嗦,但它给了我们更多的能力来控制谓词的功能。
@Test
public void givenInput_whenContainsWorks_thenCorrect2() {
int i = 5;
BiFunction<Integer, List<Integer>, Boolean> contains
= (t, u) -> u.contains(t);
String s = Match(i).of(
Case($(o -> contains
.apply(i, Arrays.asList(2, 4, 6, 8))), "Even Single Digit"),
Case($(o -> contains
.apply(i, Arrays.asList(1, 3, 5, 7, 9))), "Odd Single Digit"),
Case($(), "Out of range"));
assertEquals("Odd Single Digit", s);
}
在上面的例子中,我们创建了一个
Object Decomposition
对象分解是将一个
public class Employee {
private String name;
private String id;
//standard constructor, getters and setters
}
我们可以将
@Test
public void givenObject_whenDecomposesJavaWay_thenCorrect() {
Employee person = new Employee("Carl", "EMP01");
String result = "not found";
if (person != null && "Carl".equals(person.getName())) {
String id = person.getId();
result="Carl has employee id "+id;
}
assertEquals("Carl has employee id EMP01", result);
}
我们创建一个雇员对象,然后在应用过滤器之前首先检查它是否为空,以确保我们最终得到的是一个名字为
@Test
public void givenObject_whenDecomposesVavrWay_thenCorrect() {
Employee person = new Employee("Carl", "EMP01");
String result = Match(person).of(
Case(Employee($("Carl"), $()),
(name, id) -> "Carl has employee id "+id),
Case($(),
() -> "not found"));
assertEquals("Carl has employee id EMP01", result);
}
这两种模式都是从匹配的对象中获取值,并将它们存储到
@Unapply
static Tuple3<Integer, Integer, Integer> LocalDate(LocalDate date) {
return Tuple.of(
date.getYear(), date.getMonthValue(), date.getDayOfMonth());
}
@Test
public void givenObject_whenDecomposesVavrWay_thenCorrect2() {
LocalDate date = LocalDate.of(2017, 2, 13);
String result = Match(date).of(
Case(LocalDate($(2016), $(3), $(13)),
() -> "2016-02-13"),
Case(LocalDate($(2016), $(), $()),
(y, m, d) -> "month " + m + " in 2016"),
Case(LocalDate($(), $(), $()),
(y, m, d) -> "month " + m + " in " + y),
Case($(),
() -> "(catch all)")
);
assertEquals("month 2 in 2017",result);
}
Side Effects
默认情况下,
public void displayEven() {
System.out.println("Input is even");
}
public void displayOdd() {
System.out.println("Input is odd");
}
@Test
public void whenMatchCreatesSideEffects_thenCorrect() {
int i = 4;
Match(i).of(
Case($(isIn(2, 4, 6, 8)), o -> run(this::displayEven)),
Case($(isIn(1, 3, 5, 7, 9)), o -> run(this::displayOdd)),
Case($(), o -> run(() -> {
throw new IllegalArgumentException(String.valueOf(i));
})));
}