Scala match模式匹配


一、簡單匹配

1.基本語法

匹配字面量
①case _ :默認匹配
②如果沒有匹配到任何case,則會拋出異常scala.MatchError
③=>后如果有多個語句,可以加大括號,也可不加
④模式匹配可以有返回值

def main(args: Array[String]): Unit = {
  val n1 = 7
  val n2 = 8
  val ch = '+'

  val res = ch match {
    case '+' => n1 + n2
    case '-' => n1 - n2
    case _ =>
      println("sorry")
      println("沒有匹配到")
  }

  println(res)
}

 

2.條件守衛,if后的括號可以省略

def main(args: Array[String]): Unit = {
  val num = 8

  num match {
    case _ if num < 5 => println("小於5")
    case _ if num > 5 => println("大於5")
    case _ => println("default")
  }
}

 

3.match中匹配變量

def main(args: Array[String]): Unit = {
  val name = "mo"

  name match {
    //將name的值賦給myName,此時這個case一定會匹配到
    case myName => println(myName)
  }
}

 

4.變量類型匹配

①只有類型相同的變量才會匹配到

②匹配到的時候,相當於自動做了obj.isInstanceOf和obj.asInstanceOf的操作

def main(args: Array[String]): Unit = {
  val num = 3
  val obj = if (num == 1) Array("abc", "def", "xyz")
  else if (num == 2) Array(1, 2, 3)
  else if (num == 3) Map("abc" -> 1, "def" -> 2)

  obj match {
    case c: Array[String] => println(c.mkString(","))
    case c: Array[Int] => println(c.mkString(","))
    case c: Map[String, Int] => println(c)
    //只判斷類型,不接收值
    case _: String => println("xx")
    case _ => println("啥也不是")
  }
  
}

 

二、集合匹配

1.數組匹配

def main(args: Array[String]): Unit = {
  val array = Array(Array(1), Array(1, 2), Array(1, 2, 3))

  for (item <- array) {
    val res = item match {
      case Array(1) => 1
      case Array(x, y) => x + "," + y
      case Array(1, _*) => "以1開頭的數組"
      case _ => "default"
    }

    println(res)
  }

}

 

2.列表匹配

def main(args: Array[String]): Unit = {
  val list = List(List(1), List(1, 2), List(1, 2, 3))

  for (elem <- list) {
    elem match {
      //1 + 空
      case 1 :: Nil => 1
      //元素x + 元素y + 空
      case x :: y :: Nil => x + "," + y
      //1 + 0個或多個元素,后面的部分賦值給自定義變量
      case 1 :: tail => "以1開頭的集合"
      case _ => "default"
    }
  }

}

 

3.元組匹配

def main(args: Array[String]): Unit = {
  val tuples = Array((1, 2), (2, 1), (1, 2, 3))

  for (elem <- tuples) {
    val res = elem match {
      //匹配以1開頭的二元組
      case (1, _) => 1
      //匹配二元組  
      case (x, y) => (y, x)
      case _ => "default"
    }

    println(res)
  }
  
}

  由於Map中的元素就是一個一個二元組,所以在遍歷時,可以使用元組匹配。

def main(args: Array[String]): Unit = {
  val map = Map("A" -> 1, "B" -> 2, "C" -> 3)

  for ((k, v) <- map) {
    println(k, v)
  }

  println("-------------------")

  //只匹配v=3的元素
  for ((k, 3) <- map) {
    println(k, 3)
  }

  println("-------------------")

  //條件守衛。與模式匹配中條件守衛的位置不同。
  for ((k, v) <- map if v > 2) {
    println(k, v)
  }

}

  由此可知,在 <- 形式的for循環中,都是在對集合中的元素做模式匹配。

 

三、對象匹配

  在對象匹配中,利用對象的unapply方法提取參數,並賦值。

object MatchObjectDemo {

  def main(args: Array[String]): Unit = {
   
    val zs = new Person("zs", 10)

    /**
      * 輸出結果
      * unapply()...
      * zs
      * 10
      */
    zs match {
      // 1.調用Person的unapply(person: Person),將參數zs傳入
      // 2.判斷是否返回Some
      // 3.如果返回Some,判斷元素個數是否相等(不判斷類型)
      // 4.如果元素個數相等,則將Some里的值賦給x和y
      case Person(x, y) =>
        println(x)
        println(y)
      case _ => println("default")
    }

  }

}

class Person(_name: String, _age: Int) {
  val name = _name
  val age = _age

  override def toString = s"Person($name, $age)"
}

object Person {

  def unapply(person: Person): Option[(String, Int)] = {
    println("unapply()...")
    if (person != null) Some(person.name, person.age) else None
  }

}

  case Person(x, y)的寫法很具有迷惑性,看起來像是在調用Person的applay方法構建對象,但實際上Person只是標識了去調用哪個類的unapply方法,(x, y)只是兩個接收Some元素值的變量,僅此而已。

  與unapply相同作用的還有一個unapplySeq方法,作用與unapply相同,區別在於unapplySeq返回的Some里面的元素是一個集合。

def unapplySeq(person: Person): Option[Array[Any]] = {
    println("unapplySeq()...")
    if (person != null) Some(Array(person.name, person.age)) else None
}

  

四、變量聲明與模式匹配

  與集合匹配和對象匹配中的格式一致,通過模式匹配,可以快速聲明多個變量。

def main(args: Array[String]): Unit = {

  //數組匹配聲明變量
  val Array(first, second, _*) = Array(1, 2, 3, 4, 5)

  //列表匹配聲明變量
  val m :: n :: tail = List(1, 2, 3, 4, 5)

  //元組匹配聲明變量
  val (x, y, z) = (1, 2, "hello")

  // /% 是BigInt中的一個方法,返回一個二元組
  val (q, r) = BigInt(10) /% 3

  //對象匹配聲明變量。可以這樣寫,但沒必要。
  val Person(name, age) = new Person("zs", 10)
  println(name,age)
  
}

 

五、樣例類與模式匹配

Case Class一般被翻譯成樣例類,它是一種特殊的類,能夠被優化以用於模式匹配。
當一個類被聲名為case class的時候,scala會幫助我們做下面幾件事情:
1、構造器中的參數如果不被聲明為var的話,它默認的是val類型的,但一般不推薦將構造器中的參數聲明為var。
2、自動創建伴生對象,同時在里面給我們實現子apply方法,使我們在使用的時候可以不直接使用new創建對象。
3、伴生對象中同樣會幫我們實現unapply方法,從而可以將case class應用於模式匹配。
4、實現自己的toString、hashCode、copy、equals方法
除此之此,case class與其它普通的scala類沒有區別
object CaseClassDemo {

  def main(args: Array[String]): Unit = {
    for (elem <- Array(Dollar(1000.0), Currency(200.0, "rmb"), NoAmount)) {
      val res = elem match {
        case Dollar(v) => v
        case Currency(v, u) => v + u
        case NoAmount =>
        case _ => "default"
      }

      println(res)
    }

  }

}

abstract class Amount

//樣例類的主構造器參數會被當成全局變量 val value
case class Dollar(value: Double) extends Amount

case class Currency(value: Double, unit: String) extends Amount

case object NoAmount extends Amount

 

六、最佳實踐案例-商品捆綁打折出售

  現在有一些商品,請使用Scala設計相關的樣例類,完成商品捆綁打折出售。要求:①商品捆綁可以是單個商品,也可以是多個商品。② 打折時按照折扣x元進行設計.③ 能夠統計出所有捆綁商品打折后的最終價格。

  設計樣例類:

abstract class Item

//描述+價格
case class Book(desc: String, price: Double) extends Item

//描述+折扣+捆綁的其他項
case class Bundle(desc: String, discount: Double, item: Item*) extends Item

  一個捆綁商品的例子

val bundle = Bundle("書籍", 10, Book("漫畫", 40), Bundle("文學作品", 20, Book("陽關", 80), Book("圍城", 30)))

  知識點1:將desc綁定到第一個book的描述“漫畫”

def main(args: Array[String]): Unit = {
  val bundle = Bundle("書籍", 10, Book("漫畫", 40), Bundle("文學作品", 20, Book("陽關", 80), Book("圍城", 30)))

  val res = bundle match {
    case Bundle(_, _, Book(desc, _), _*) => desc
  }
  println(res)

}

  知識點2:通過@表示法將嵌套的值綁定到變量。_*綁定剩余Item到rest

def main(args: Array[String]): Unit = {
  val bundle = Bundle("書籍", 10, Book("漫畫", 40), Bundle("文學作品", 20, Book("陽關", 80), Book("圍城", 30)))

  val res = bundle match {
    case Bundle(_, _, book@Book(_, _), rest@_*) => (book,rest)
  }

  println(res._1) //Book(漫畫,40.0)
  println(res._2) //ArraySeq(Bundle(文學作品,20.0,ArraySeq(Book(陽關,80.0), Book(圍城,30.0))))

}

  最終解答

object CaseClassDemo {

  def main(args: Array[String]): Unit = {
    val bundle = Bundle("書籍", 10, Book("漫畫", 40), Bundle("文學作品", 20, Book("陽關", 80), Book("圍城", 30)))

    val total = price(bundle)

    println(total)  //120.0

  }

  //求捆綁書籍的總價格
  def price(item: Item):Double={
    item match {
      //單本書沒有折扣,直接返回價格
      case Book(_,price)=>price
      //捆綁書將捆綁的每一個項的價格分別求出,求和,再減去折扣
      case Bundle(_,discount,items@_*)=>items.map(price).sum-discount
    }
  }

}

  

  






免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM