聊聊 scala 的模式匹配

阿里云云棲社群發表於2019-01-19

一. scala 模式匹配(pattern matching)

pattern matching 可以說是 scala 中十分強大的一個語言特性,當然這不是 scala 獨有的,但這不妨礙它成為 scala 的語言的一大利器。

scala 的 pattern matching 是類似這樣的,

e match {
  case Pattern1 => do Something
  case Pattern2 if-clause => do others
  ...
}

其中,變數 e 後面接一個 match 以及一個程式碼塊,其中每個 case 對應一種可能回匹配的型別,如果匹配成功則執行 => 後面的程式碼。

我們可以用一個具體一些的例子來看看模式匹配是怎麼工作的:

case class Player(name: String, score: Int)
def printMessage(player: Player) = player match {
  case Player(_, score) if score > 100000 =>
    println("Get a job, dude!")
  case Player(name, _) =>
    println("Hey, $name, nice to see you again!")
}

看起來有點類似於其他語言的 switch,但其實還是有很大的不同的。

以java 的 switch 為例,java 的 switch 僅僅會做一些基本型別的匹配,然後執行一些動作,並且是沒有返回值的。

而 scala 的 pattern matching match 則要強大得多,除了可以匹配數值,同時它還能匹配型別。

def parseArgument(arg: String) = arg match {
    //匹配值
    case "-h" | "--help" => displayHelp
    case "-v" | "--version" => displayVerion
    case whatever => unknownArgument(whatever)
}
def f(x: Any): String = x match {
    //匹配型別
    case i:Int => "integer: " + i
    case _:Double => "a double"
    case s:String => "I want to say " + s
}

同時 pattern matching 是有返回值的,比如上面的 match ,它返回的就是一個 Unit。我們也可以修改上面的程式碼讓它返回一個字串:

case class Player(name: String, score: Int)
def message(player: Player) = player match {
  case Player(_, score) if score > 100000 =>
    "Get a job, dude!"
  case Player(name, _) =>
    "Hey, $name, nice to see you again!"
}

值得一提的是, pattern matching 返回值是由第一個匹配的模式中的程式碼塊決定的。

二. 為什麼要用 pattern matching

看到這裡你會發現一個問題, pattern matching 不是和if else 差不多嗎?那為什麼還要使用 pattern matching 呢?

首先我們需要明白,模式匹配其實本質上是提供一個方便的解構 (Destructuring) 資料結構的方式,以 scala 為例, pattern matching 其實用到了 scala 中提取器的功能, 提取器其實就是類中的 unapply () 方法。

trait User {
  def name: String
}
class FreeUser(val name: String) extends User
object FreeUser {
  //提取器
  def unapply(user: FreeUser): Option[String] = Some(user.name)
}
  val user: User = new FreeUser("Daniel")
  user match {
    case FreeUser(name) => println("it match here" + name)
    case _ => println("not me")
  }

明白了模式匹配的本質你就會直到,其實 if else 只是 pattern matching 中的一個典型的用法,但並非它的全部。

同時, pattern matching 允許你解耦兩個並不真正屬於彼此的東西,也使得你的程式碼更易於測試。比如上面的 match 部分的程式碼我們可以寫成下面這樣:

  val user: User = new FreeUser("Daniel")
  //將返回結果存在一個常量中
  val message = user match {
    case FreeUser(name) => "it match here" + name
    case _ => "not me"
  }
  //可以隨意使用該常量,實現解耦
  println(message)

這樣會賦予程式碼更多的靈活性,同時也更加方便做進一步操作。

而以可讀性的角度來說,使用一大堆的 if else 程式碼無疑是比較難看的,而如果使用 pattern matching 的話,程式碼會簡潔清晰很多,而簡潔的程式碼則會更容易閱讀。

參考文章:


本文作者:終日而思一

閱讀原文

本文為雲棲社群原創內容,未經允許不得轉載。

相關文章