Skip to content

Commit 37bcf63

Browse files
authored
Merge pull request #1177 from declan94/pattern_matching
zh-cn for Scala Toor: Pattern matching
2 parents b787563 + d1e3466 commit 37bcf63

File tree

1 file changed

+140
-1
lines changed

1 file changed

+140
-1
lines changed

_zh-cn/tour/pattern-matching.md

Lines changed: 140 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
layout: tour
3-
title: Pattern Matching
3+
title: 模式匹配
44

55
discourse: false
66

@@ -13,3 +13,142 @@ language: zh-cn
1313
next-page: singleton-objects
1414
previous-page: case-classes
1515
---
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

Comments
 (0)