Scala學習筆記——case模式匹配

簡單匹配

 簡單匹配Boolean值

val boolSeq = Seq(true, false)
  for (bool <- boolSeq) {
    bool match {
      case true => println("hi true")
      case false => println("hi false")
    }
  }

match中的值、變量和類型

 匹配特定類型的幾個值:

 for {
    x <- Seq(1, 2, 2.7, "one", "two", 'four)
  } {
    val str = x match {
      case 1          => "int 1"
      case i: Int     => "other int : " + i
      case d: Double  => "a double: " + d
      case "one"      => "string one"
      case s: String  => "other string:" + s
      case unexpected => "unexception value :" + unexpected
    }
    println(str)
  }

case _在結尾作爲默認子句,用來匹配任意輸入值。

for {
    x <- Seq(1, 2, 2.7, "one", "two", 'four)
  } {
    val str = x match {
      case 1          => "int 1"
      case _: Int     => "other int" + x
      case _: Double  => "other double" + x
      case "one"      => "string one"
      case _: String  => "other string:" + x
      case _          => "unexpected value: " + x
    }
    println(str)
  }

  case y 的含義其實就是匹配所有輸入(由於這裏沒有類型註解),並將其賦值給新的變量y.這裏的y沒有被解釋爲方法參數y.因此,事實上我們將一個默認的,匹配一切的語句寫在了第一個,導致系統給出了這條“變量型匹配語句”會匹配一切輸入的警告。代碼也從未執行到第二條case語句,於是就得到了兩條關於不可達代碼的警告

def checkX(y: Int) = {
    for {
      x <- Seq(99, 100, 101)
    } {
      val str = x match {
        case y      => "found y!"
        case i: Int => "int:" + i
      }
      println(str)
    }
  }

  checkX(999)
  println("------------- check y")

輸出結果

------------- check x
found y!
found y!
found y!

 case 字句中,以小寫字母開頭的標識符被認爲是用來提取待匹配值的新變量。如果需要引用之前已經定義的變量時,使用反引號將其包圍。 於此相對,以大寫字母開頭的標識符被認爲是類型名稱:

  def checkY(y: Int) = {
    for {
      x <- Seq(99, 100, 101)
    } {
      val str = x match {
        case `y`    => "found y!"
        case i: Int => "int:" + i
      }
      println(str)
    }
  }

  checkY(99)
------------- check y
found y!
int:100
int:101

 case字句也持“或”邏輯 :|

/**
    * case字句也持“或”邏輯 :|
    */
  for {
    x <- Seq(1, 2, 2.7, "one", "two", 'four)
  } {
    val str = x match {
      case _: Int | _: Double   => "a number: " + x
      case "one"                => "String one"
      case _: String            => "other string:" + x
      case _                    => "unexpected value:" + x
    }
    println(str)
  }


序列的模式匹配

/**
  * 序列的模式匹配
  */

object SeqCaseApp extends App {

  val nonEmptySeq = Seq(1, 2, 3, 4, 5)

  val emptySeq = Seq.empty[Int]

  val nonEmptyList = List(1, 2, 3, 4, 5)

  val emptyList = Nil

  val nonEmptyVector = Vector(1, 2, 3, 4, 5)

  val emptyVector = Vector.empty[Int]

  val nonEmptyMap = Map("one" -> 1, "two" -> 2, "three" -> 3)

  val emptyMap = Map.empty[String, Int]

  //定義了一個遞歸方法,從Seq[T]中構造String,T爲某種待定的類型。方法體是用來與輸入的Seq[T]相匹配
  def seqToString[T](seq: Seq[T]): String = seq match {
    case head +: tail => s"$head +: " + seqToString(tail)
    case Nil          => "Nil"
  }

  for (seq <- Seq(nonEmptySeq, emptySeq, nonEmptyList, emptyList, nonEmptyVector, emptyVector, nonEmptyMap.toSeq, emptyMap.toSeq)) {
    println(seqToString(seq))
  }
}

元祖的匹配

 掃描元組的字面量,很容易對元祖進行匹配:

package base.caseT

object TupleCaseApp extends App {

  val langs = Seq(
    ("Scala", "Java", "Clojure"),
    ("Clojure", "Rich", "python")
  )

  for (tuple <- langs) {
    tuple match {
      case ("Scala", _, _)     => println("found scala")
      case (lang, first, last) => println(s"found other language : $lang ($first, $last)")
    }
  }

  // 打印1,2
  (1,2) match {
    case (a,b) => println(a+","+b)
  }

  // 打印2
  (1,2) match {
    case (1, b) => println(b)
  }

  // 打印found
  (1,2) match {
    case (_, 2) => println("found")
  }


  // 得到List(1,2)
  List(1,2,3,4) match {
    case a :: b :: other => List(a, b)
    case _ => List()
  }
  /**
    * guard 語句
    */
  for (i <- Seq(1, 2, 3, 4)) {
    i match {
      case _ if i % 2 == 0 => println(s"even: $i")
      case _               => println(s"odd: $i")
    }
  }

}

case類的匹配

 case類的匹配,可以對case類對象的內容進行考察:

package base.caseT

/**
  * 匹配嵌套類型的內容
  */

object ClassCaseApp extends App {

  case class Address(street: String, city: String, country: String)

  case class Person(name: String, age: Int, address: Address)

  val alice = Person("Alice", 25, Address("101", "杭州", "拱墅區"))
  val bob = Person("Bob", 29, Address("102", "北京", "望京"))
  val marry = Person("Marry", 25, Address("103", "南京", "棲霞區"))

  for(person <- Seq(alice, bob, marry)) {
    person match {
      case Person("Alice", 25, Address(_, "杭州", _))     => println("Hi Alice")
      case Person("Bob", 29, Address(_, "北京", _))       => println("Hi Bob")
      case Person(name, age, _)                          => println(s"who are you, $age year-old person named $name?")
    }
  }
}

case class執行原理

 當一個類被聲名爲case class的時候,scala會幫助我們做下面幾件事情:

  1. 構造器中的參數如果不被聲明爲var的話,它默認的話是val類型的,但一般不推薦將構造器中的參數聲明爲var
  2. 自動創建伴生對象,同時在裏面給我們實現子apply方法,使得我們在使用的時候可以不直接顯示地new對象
  3. 伴生對象中同樣會幫我們實現unapply方法,從而可以將case class應用於模式匹配.
  4. 實現自己的toString、hashCode、copy、equals方法 .
     手動創建三個scala腳本。反編譯試驗下:
package base

 abstract class A
package base

case class B(name:String, age:Int) extends base.A
case object CaseObject extends base.A{

}

執行命令編譯腳本:

scalac A.scala
scalac B.scala
scalac CaseObject.scala

case class B反編譯如下:

pjx@pjxdeMacBook-Pro:~/program/scala-2.12.5/bin/base$  javap -private B.class
Compiled from "B.scala"
public class base.B extends base.A implements scala.Product,scala.Serializable {
  private final java.lang.String name;
  private final int age;
  public static scala.Option<scala.Tuple2<java.lang.String, java.lang.Object>> unapply(base.B);
  //自動生成半生對象
  public static base.B apply(java.lang.String, int);
  public static scala.Function1<scala.Tuple2<java.lang.String, java.lang.Object>, base.B> tupled();
  public static scala.Function1<java.lang.String, scala.Function1<java.lang.Object, base.B>> curried();
  public java.lang.String name();
  public int age();
  public base.B copy(java.lang.String, int);
  public java.lang.String copy$default$1();
  public int copy$default$2();
  public java.lang.String productPrefix();
  public int productArity();
  public java.lang.Object productElement(int);
  public scala.collection.Iterator<java.lang.Object> productIterator();
  public boolean canEqual(java.lang.Object);
  public int hashCode();
  public java.lang.String toString();
  public boolean equals(java.lang.Object);
  public base.B(java.lang.String, int);
}

CaseObject反編譯如下:

pjx@pjxdeMacBook-Pro:~/program/scala-2.12.5/bin/base$  javap -private ../CaseObject.class
Compiled from "CaseObject.scala"
public final class CaseObject {
  public static java.lang.String toString();
  public static int hashCode();
  public static boolean canEqual(java.lang.Object);
  public static scala.collection.Iterator<java.lang.Object> productIterator();
  public static java.lang.Object productElement(int);
  public static int productArity();
  public static java.lang.String productPrefix();
}


pjx@pjxdeMacBook-Pro:~/program/scala-2.12.5/bin/base$  javap -private ../CaseObject$.class
Compiled from "CaseObject.scala"
public final class CaseObject$ extends base.A implements scala.Product,scala.Serializable {
  public static CaseObject$ MODULE$;
  public static {};
  public java.lang.String productPrefix();
  public int productArity();
  public java.lang.Object productElement(int);
  public scala.collection.Iterator<java.lang.Object> productIterator();
  public boolean canEqual(java.lang.Object);
  public int hashCode();
  public java.lang.String toString();
  private java.lang.Object readResolve();
  private CaseObject$();
}

case objectcase class 不同的是,沒有applyunapply方法,這是因爲None不需要創建對象及進行內容提取。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章