1
1
---
2
2
layout : tour
3
- title : Pattern Matching
3
+ title : 模式匹配
4
4
5
5
discourse : false
6
6
@@ -13,3 +13,142 @@ language: zh-cn
13
13
next-page : singleton-objects
14
14
previous-page : case-classes
15
15
---
16
+
17
+ 模式匹配是检查某个值(value)是否匹配某一个模式的机制,一个成功的匹配同时会将匹配值解构为其组成部分。它是Java中的` switch ` 语句的升级版,同样可以用于替代一系列的 if/else 语句。
18
+
19
+ ## 语法
20
+ 一个模式匹配语句包括一个待匹配的值,` match ` 关键字,以及至少一个` case ` 语句。
21
+
22
+ ``` tut
23
+ import scala.util.Random
24
+
25
+ val x: Int = Random.nextInt(10)
26
+
27
+ x match {
28
+ case 0 => "zero"
29
+ case 1 => "one"
30
+ case 2 => "two"
31
+ case _ => "many"
32
+ }
33
+ ```
34
+ 上述代码中的` val x ` 是一个0到10之间的随机整数,将它放在` match ` 运算符的左侧对其进行模式匹配,` match ` 的右侧是包含4条` case ` 的表达式,其中最后一个` case _ ` 表示匹配其余所有情况,在这里即是` x ` 大于2的情况。
35
+
36
+ ` match ` 表达式具有一个结果值
37
+ ``` tut
38
+ def matchTest(x: Int): String = x match {
39
+ case 1 => "one"
40
+ case 2 => "two"
41
+ case _ => "many"
42
+ }
43
+ matchTest(3) // many
44
+ matchTest(1) // one
45
+ ```
46
+ 这个` match ` 表达式是String类型的,因为所有的情况(case)均返回String,所以` matchTest ` 函数的返回值是String类型。
47
+
48
+ ## 案例类(case classes)的匹配
49
+
50
+ 案例类非常适合用于模式匹配。
51
+
52
+ ``` tut
53
+ abstract class Notification
54
+
55
+ case class Email(sender: String, title: String, body: String) extends Notification
56
+
57
+ case class SMS(caller: String, message: String) extends Notification
58
+
59
+ case class VoiceRecording(contactName: String, link: String) extends Notification
60
+
61
+
62
+ ```
63
+
64
+ ` Notification ` 是一个虚基类,它有三个具体的子类` Email ` , ` SMS ` 和` VoiceRecording ` ,我们可以在这些案例类(Case Class)上像这样使用模式匹配:
65
+
66
+ ```
67
+ def showNotification(notification: Notification): String = {
68
+ notification match {
69
+ case Email(email, title, _) =>
70
+ s"You got an email from $email with title: $title"
71
+ case SMS(number, message) =>
72
+ s"You got an SMS from $number! Message: $message"
73
+ case VoiceRecording(name, link) =>
74
+ s"you received a Voice Recording from $name! Click the link to hear it: $link"
75
+ }
76
+ }
77
+ val someSms = SMS("12345", "Are you there?")
78
+ val someVoiceRecording = VoiceRecording("Tom", "voicerecording.org/id/123")
79
+
80
+ println(showNotification(someSms)) // prints You got an SMS from 12345! Message: Are you there?
81
+
82
+ println(showNotification(someVoiceRecording)) // you received a Voice Recording from Tom! Click the link to hear it: voicerecording.org/id/123
83
+ ```
84
+ ` showNotification ` 函数接受一个抽象类` Notification ` 对象作为输入参数,然后匹配其具体类型。(也就是判断它是一个` Email ` ,` SMS ` ,还是` VoiceRecording ` )。在` case Email(email, title, _) ` 中,对象的` email ` 和` title ` 属性在返回值中被使用,而` body ` 属性则被忽略,故使用` _ ` 代替。
85
+
86
+ ## 模式守卫(Pattern gaurds)
87
+ 为了让匹配更加具体,可以使用模式守卫,也就是在模式后面加上` if <boolean expression> ` 。
88
+ ```
89
+
90
+ def showImportantNotification(notification: Notification, importantPeopleInfo: Seq[String]): String = {
91
+ notification match {
92
+ case Email(email, _, _) if importantPeopleInfo.contains(email) =>
93
+ "You got an email from special someone!"
94
+ case SMS(number, _) if importantPeopleInfo.contains(number) =>
95
+ "You got an SMS from special someone!"
96
+ case other =>
97
+ showNotification(other) // nothing special, delegate to our original showNotification function
98
+ }
99
+ }
100
+
101
+ val importantPeopleInfo = Seq("867-5309", "[email protected] ")
102
+
103
+ val someSms = SMS("867-5309", "Are you there?")
104
+ val someVoiceRecording = VoiceRecording("Tom", "voicerecording.org/id/123")
105
+ val importantEmail = Email("[email protected] ", "Drinks tonight?", "I'm free after 5!")
106
+ val importantSms = SMS("867-5309", "I'm here! Where are you?")
107
+
108
+ println(showImportantNotification(someSms, importantPeopleInfo))
109
+ println(showImportantNotification(someVoiceRecording, importantPeopleInfo))
110
+ println(showImportantNotification(importantEmail, importantPeopleInfo))
111
+ println(showImportantNotification(importantSms, importantPeopleInfo))
112
+ ```
113
+
114
+ 在` case Email(email, _, _) if importantPeopleInfo.contains(email) ` 中,除了要求` notification ` 是` Email ` 类型外,还需要` email ` 在重要人物列表` importantPeopleInfo ` 中,才会匹配到该模式。
115
+
116
+
117
+ ## 仅匹配类型
118
+ 也可以仅匹配类型,如下所示:
119
+ ``` tut
120
+ abstract class Device
121
+ case class Phone(model: String) extends Device{
122
+ def screenOff = "Turning screen off"
123
+ }
124
+ case class Computer(model: String) extends Device {
125
+ def screenSaverOn = "Turning screen saver on..."
126
+ }
127
+
128
+ def goIdle(device: Device) = device match {
129
+ case p: Phone => p.screenOff
130
+ case c: Computer => c.screenSaverOn
131
+ }
132
+ ```
133
+ 当不同类型对象需要调用不同方法时,仅匹配类型的模式非常有用,如上代码中` goIdle ` 函数对不同类型的` Device ` 有着不同的表现。一般使用类型的首字母作为` case ` 的标识符,例如上述代码中的` p ` 和` c ` ,这是一种惯例。
134
+
135
+ ## 密封类
136
+
137
+ 特质(trait)和类(class)可以用` sealed ` 标记为密封的,这意味着其所有子类都必须与之定义在相同文件中,从而保证所有子类型都是已知的。
138
+
139
+ ``` tut
140
+ sealed abstract class Furniture
141
+ case class Couch() extends Furniture
142
+ case class Chair() extends Furniture
143
+
144
+ def findPlaceToSit(piece: Furniture): String = piece match {
145
+ case a: Couch => "Lie on the couch"
146
+ case b: Chair => "Sit on the chair"
147
+ }
148
+ ```
149
+ 这对于模式匹配很有用,因为我们不再需要一个匹配其他任意情况的` case ` 。
150
+
151
+ ## 备注
152
+
153
+ Scala的模式匹配语句对于使用[ 案例类(case classes)] ( case-classes.html ) 表示的类型非常有用,同时也可以利用[ 提取器对象(extractor objects)] ( extractor-objects.html ) 中的` unapply ` 方法来定义非案例类对象的匹配。
154
+
0 commit comments