1. 程式人生 > >Scala之模式匹配和樣例類

Scala之模式匹配和樣例類

1. 模式匹配

1.1. 更好的 Switch

Scala 中類似 Java 的 switch 程式碼:

object PatternDemo {

  def main(args: Array[String]): Unit = {
    var sign = 0
    val ch: Char  = 'p'
    val valchar = 'p'
    var digit = 0

   //match 是表示式
    ch match {
      case '+' => sign = 1
      case '-' => sign = -1
      //使用|分割多個選項
      case '*' | 'x' => sign = 2
      //可以使用變數
      case valchar => sign = 3
      //case _ 類似Java中的default
      // 如果沒有模式能匹配,會丟擲MacthError
      //可以給模式新增守衛
      case _ if Character.isDigit(ch) => digit = Character.digit(ch, 10)
    }
    println("sign = "+ sign)
  }

}

1.2. 樣例類匹配

//定義樣例類
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

//基於樣例類的模式匹配
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))  //結果: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

1.3. 帶守衛的模式

增加布爾表示式使得匹配更具體。

def showImportantNotification(notification: Notification, importantPeopleInfo: Seq[String]): String = {
  notification match {
  //僅匹配email在importantPeople列表裡的內容
    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))

1.4. 型別匹配

val arr = Array("hello", 1, 2.0, 'a')
val obj = arr(Random.nextInt(4))
obj match {
  case x: Int => x
  case s: String => Integer.parseInt(s)
  case _: BigInt => Int.MaxValue
  case _ => 0
}

1.5. 匹配陣列、列表、元組

陣列匹配

val arr1 = Array(1,1)
  val res = arr1 match {
  case Array(0) => "0"
  //匹配包含0的陣列
  case Array(x, y) => s"$x $y"
  // 匹配任何帶有兩個元素的陣列,並將元素繫結到x和y
  case Array(0, _*) => "0..."
  //匹配任何以0開始的陣列
  case _ => "something else"
}

列表匹配

val lst = List(1,2)
val res2 =  list match {
   case 0 :: Nil => "0"
   case x :: y :: Nil => x + " " + y
   case 0 :: tail => "0 ..."
   case _ => "something else"
 }

元組匹配

var pair = (1,2)
val res3 =  pair match {
  case (0, _)  => "0 ..."
  case (y, 0) => s"$y 0"
  case _ => "neither is 0"
}

2. Sealed 類 (密封類)

Scala 中,Traits 和 class 可以被關鍵字 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"
}

3. 樣例類

在 Scala 中樣例類是一中特殊的類,樣例類是不可變的,可以通過值進行比較,可用於模式匹配。
定義一個樣例類:

case class Point(x: Int, y: Int)

建立樣例類物件:

val point = Point(1, 2)
val anotherPoint = Point(1, 2)
val yetAnotherPoint = Point(2, 2)

通過值對樣例類物件進行比較:

if (point == anotherPoint) {
  println(point + " and " + anotherPoint + " are the same.")
} else {
  println(point + " and " + anotherPoint + " are different.")
}
// Point(1,2) 和 Point(1,2)一樣的.

if (point == yetAnotherPoint) {
  println(point + " and " + yetAnotherPoint + " are the same.")
} else {
  println(point + " and " + yetAnotherPoint + " are different.")
}
// Point(1,2)和Point(2,2)是不同的.

在模式匹配中使用樣例類:

abstract class Amount
// 繼承了普通類的兩個樣例類
case class Dollar(value: Double) extends Amount
case class Currency(value: Double, unit: String) extends Amount
case object Nothing extends Amount
object CaseClassDemo {

  def main(args: Array[String]): Unit = {
    val amt = new Dollar(10);
    patternMatch(amt)
  }
  def patternMatch(amt: Amount) {
    amt match {
      case Dollar(v) => println("$" + v)
      case Currency(_, u) => println("Oh noes, I got " + u)
      case Nothing => println("nothing") //樣例物件沒有()
    }
  }
}

宣告樣例類 ,以下幾件事會自動發生:

(1) 構造器中每一個引數都是 val,除非顯示地宣告為 var  
(2) 伴生物件提供 apply ,讓你不使用 new 關鍵字就能構造出相應的物件
(3) 提供 unapply 方法,讓模式匹配可以工作  
(4) 生成 toString,equals,hashCode,copy 方法,除非顯示給出這些方法的定義。

4. Option 型別

在 Scala 中 Option 型別樣例類用來表示可能存在或也可能不存在的值( Option 的子類有 Some 和 None )。Some 包裝了某個值,None 表示沒有值。

object OptionDemo {
  def main(args: Array[String]) {
    val map = Map("a" -> 1, "b" -> 2)
    val v = map.get("b") match {
      case Some(i) => i
      case None => 0
    }
    println(v)
    //更好的方式
    val v1 = map.getOrElse("c", 0)
    println(v1)
  }
}

5. 偏函式

被包在花括號內沒有 match 的一組 case 語句是一個偏函式,它是 PartialFunction[A, B] 的一個例項,A 代表引數型別,B 代表返回型別,常用作輸入模式匹配。

object PartialFunctionDemo {
  def f: PartialFunction[String, Int] = {
    case "one" => 1
    case "two" => 2
   // case _ => -1
  }

  def main(args: Array[String]) {
    //呼叫f.apply("one")
    println(f("one"))
    println(f.isDefinedAt("three"))
    //丟擲MatchError
    println(f("three"))
  }
}