Scala之旅(TOUR OF SCALA)——模式匹配(PATTERN MATCHING)

模式匹配是一种检查值是否匹配某种模式的机制。成功的匹配也可以将价值解构成其组成部分(这句翻译有问题)[6]。这是一个比 Java 中的 switch 更强大的版本,也同样被用来代替一系列的 if/else 结构。

语法 (Syntax)

一个 match 表达式有一个待匹配的值, match 关键字和至少一个 case 表达式组成。

import scala.util.Random

val x: Int = Random.nextInt(10)

x match {
  case 0 => "zero"
  case 1 => "one"
  case 2 => "two"
  case _ => "many"
}

这个常量 x 是从0到10随机出来的。xmatch 表达式的左操作数。右边则是由四个 case 组成的表达式。最后一个 case _ 是一个对不满足上面所有条件的默认 case。

match 表达式有一个返回值。

 def matchTest(x: Int): String = x match {
  case 1 => "one"
  case 2 => "two"
  case _ => "many"
}
matchTest(3)  // many
matchTest(1)  // one

这个 match 表达式有一个 String 类型的返回值,因为所有的 case 都返回一个 String 。所以,函数 matchTest 的返回也是一个 String。

匹配条件类(Matching on case classes)

条件类在模式匹配中特别有用。

abstract class Notification

case class Email(sender: String, title: String, body: String) extends Notification

case class SMS(caller: String, message: String) extends Notification

case class VoiceRecording(contactName: String, link: String) extends Notification

Notification 是一个抽象父类,它有三个条件类分别是 EmailSMSVoiceRecording 。现在我们可以使用这些条件类来进行模式匹配。

def showNotification(notification: Notification): String = {
  notification match {
    case Email(email, title, _) =>
      s"You got an email from $email with title: $title"
    case SMS(number, message) =>
      s"You got an SMS from $number! Message: $message"
    case VoiceRecording(name, link) =>
      s"you received a Voice Recording from $name! Click the link to hear it: $link"
  }
}
val someSms = SMS("12345", "Are you there?")
val someVoiceRecording = VoiceRecording("Tom", "voicerecording.org/id/123")

println(showNotification(someSms))  // prints You got an SMS from 12345! Message: Are you there?

println(showNotification(someVoiceRecording))  // you received a Voice Recording from Tom! Click the link to hear it: voicerecording.org/id/123

函数 showNotification 需要一个 Notification 类型的参数,匹配函数中不同 Notification 子类(i.e. 可能是一个 EmailSMS,或者VoiceRecording)。在 case Email(email, title, _) 表达式中属性 emailtitle 被在下一行返回值的表达式中使用而属性 _ 则将被忽略。

模式卫兵(Pattern guards)

模式卫兵是一种简单和布尔类型的表达式,让 case 语句更加具体。只需要在匹配模式后面添加一个 if <boolean expression> 即可。

def showImportantNotification(notification: Notification, importantPeopleInfo: Seq[String]): String = {
  notification match {
    case Email(email, _, _) if importantPeopleInfo.contains(email) =>
      "You got an email from special someone!"
    case SMS(number, _) if importantPeopleInfo.contains(number) =>
      "You got an SMS from special someone!"
    case other =>
      showNotification(other) // nothing special, delegate to our original showNotification function
  }
}

val importantPeopleInfo = Seq("867-5309", "[email protected]")

val someSms = SMS("867-5309", "Are you there?")
val someVoiceRecording = VoiceRecording("Tom", "voicerecording.org/id/123")
val importantEmail = Email("[email protected]", "Drinks tonight?", "I'm free after 5!")
val importantSms = SMS("867-5309", "I'm here! Where are you?")

println(showImportantNotification(someSms, importantPeopleInfo))
println(showImportantNotification(someVoiceRecording, importantPeopleInfo))
println(showImportantNotification(importantEmail, importantPeopleInfo))
println(showImportantNotification(importantSms, importantPeopleInfo))

case Email(email, _, _) if importantPeopleInfo.contains(email) 中,模式只匹配在 importantPeopleInfo 中包含的 emailNotification

只匹配类型 (Matching on type only)

你可以只匹配类型就像下面一样:

abstract class Device
case class Phone(model: String) extends Device{
    
    
  def screenOff = "Turning screen off"
}
case class Computer(model: String) extends Device {
    
    
  def screenSaverOn = "Turning screen saver on..."
}

def goIdle(device: Device) = device match {
  case p: Phone => p.screenOff
  case c: Computer => c.screenSaverOn
}

方法 def goIdleDevice 的类型不同而有不同的行为。当 case 语句需要调用模式中的方法时,这是很有用的。使用类型的第一个字母作为 case 语句中常量的标识符是一种惯例(上面例子中的 pc)。

密封类 (Sealed classes)

特征和类可以在关键字之前添加 sealed 关键字,意思为所有的子类必须与父类在同一个文件中声明。这保证了所有的子类都是已知的。

sealed abstract class Furniture
case class Couch() extends Furniture
case class Chair() extends Furniture

def findPlaceToSit(piece: Furniture): String = piece match {
  case a: Couch => "Lie on the couch"
  case b: Chair => "Sit on the chair"
}

对模式匹配来说这是有用的,因为我们不再需要默认的 case other 语句。

笔记 (Notes)

Scala 中的模式匹配语句在匹配基于代数类型的条件类时是最有用的。Scala 也允许定义独立模式条件类,使用提取器对象(extractor objects)的 unapply 方法。

Guess you like

Origin blog.csdn.net/cuipp0509/article/details/79292398