Scala中的模式匹配(Pattern matching)

本文的内容源于对Scala官网关于Pattern matching的教程的翻译。

PATTERN MATCHING

模式匹配是一种根据模式(pattern)检查值(value)的机制。A successful match can also deconstruct a value into its constituent parts. 它是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的随机整数。x成为match运算符的左操作数,最右边是一个包含4个case的表达式。最后一个case _是一个“其余(catch all)”case,在这里表示大于2的任何数字。

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

这个match表达式中所有case都返回String,所以matchTest函数返回一个String。

case类的匹配

case类对模式匹配特别有用。

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是一个抽象的超类,有三个具体实现的case类EmailSMSVoiceRecording。现在我们可以对这些case类进行模式匹配:

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的类型(例如,判断是否是一个EmailSMSVoiceRecording)。在case Email(email, title, _)中,emailtitle字段被返回值引用,而body(_)字段被忽略。

Pattern guards

Pattern guard是简单的布尔表达式,使得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在重要人物列表中时,该模式才被匹配。

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 goIdle根据Device类型的不同有不同的表现。当case需要调用pattern上的方法时,这是很有用的。使用类名的首字母作为case的标识符是一种惯例(例如这里的pc)。

Sealed classes

特征(trait,类似Java中的interface)和类可以标记为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"
}

这对模式匹配很有用,因为我们不需要“catch all”case。

Notes

Scala的模式匹配语句对于匹配用case类表示的代数类型很有用。Scala还允许用extractor objects的unapply方法定义模式。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值