模式匹配

模式匹配是检查一个值是否匹配某种模式的机制。一个成功的匹配可以把一个值解析成它的组成部分。它相当于 Java 中 switch 的增强版,可以以此来替代一堆的 if/else 语句。

语法

一个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"
}

上面的 val x 是0到10之间的要给随机整数。 xmatch 操作符的左操作数,右边是4个样例组成的表达式。最后一个样例 _ 匹配了大于2的所有数字。这些样例也被称为代值

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 。

匹配样例类

样例类在模式匹配中特别有用。

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 是一个抽象父类,另外有3个 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 类型(它会判断出到底是一个 EmailSMS ,还是一个 VoiceRecording )。在 case Email(email, title, _)emailtitle 用于返回值,而 body 则因 _ 而被忽略。

模式守卫

模式守卫是简单的布尔表达式,使得 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", "jenny@gmail.com")

val someSms = SMS("867-5309", "Are you there?")
val someVoiceRecording = VoiceRecording("Tom", "voicerecording.org/id/123")
val importantEmail = Email("jenny@gmail.com", "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) 中,仅当 email 在重要人士的的列表中,该模式才会匹配上。

类型匹配

你可以像下面这样匹配类型:

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 goIdle 根据 Device 的类型不同会有不同的行为。当模式中需要调用方法时,这会很有用。一般惯例是使用类型的首字母来作为 case 标识符(如本例子中的 pc )。

密封类

特质和类都可以被标记为 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 all”子句了。

注意

Scala 的模式匹配语句对于由样例类表示的代数类型的匹配时很有用。另外 Scala 也允许通过使用提取器对象unapply 方法,来定义有别于样例类的的模式。

知识共享许可协议 BY 小鹏            此页面修订于 2019-07-17 14:19:05

results matching ""

    No results matching ""