美文网首页Scala Tour
[译]Scala模式匹配

[译]Scala模式匹配

作者: steanxy | 来源:发表于2017-05-07 16:33 被阅读16次

    模式匹配是根据模式检查值的机制。一个成功的匹配可以将值分解成其组成部分。它Java switch语句的一个更强大的版本,也可用于替代一系列if/else语句。

    语法

    一个匹配表达式有一个值,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个cases的表达式。最后一个case _是一个"catch all",代表了任意大于2的数字。Cases可被称为选择。

    匹配表达式有一个值。

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

    这是一个字符串匹配表达式,所有的cases都返回字符串,因此,matchTest函数返回一个字符串。

    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是一个抽象父类,有三个具体的Notification类型,分别是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使用_进行了忽略。

    模式守护

    模式守护是简单的布尔表达式,用于让情况更加具体。只要在模式后面添加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在important people列表中才能匹配模式。

    只匹配类型

    可以按照如下方式匹配类型:

    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需要调用模式的方法时非常有用。习惯使用类型的第一个字符作为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"
    }
    

    因为我们不需要"catch all" case,所以这对模式匹配非常有用。

    Notes

    Scala的模式匹配语句对于通过Case类表达的代数类型匹配非常有用。Scala也允许Case类独立定义模式,在extractor objects中使用unapply方法。

    相关文章

      网友评论

        本文标题:[译]Scala模式匹配

        本文链接:https://www.haomeiwen.com/subject/mdoutxtx.html