Scalaのパターンマッチ

参考サイト
Scala matchメモ(Hishidama's Scala match Memo)
scala-hackathon/patternmatching.rst at master · yuroyoro/scala-hackathon · GitHub

基本

match式は、switch文のような式です。breakは必要ありません。_はワイルドカードです。

def signal(color: String): String = color match {
  case "blue" => "go!"
  case "red" => "stop!"
  case "yellow" => "watch out!"
  case _ => "other color"
}

scala> signal("red")
res0: String = stop!

scala> signal("orange")
res1: String = other color

型によるマッチ

case 変数名: 型名で、型によるパターンマッチを行えます。
この変数は、マッチした場合の処理で使用することができます。

def patternMatch(arg: Any): Unit = arg match {
  case i: Int => println(s"arg is Int. toDouble: ${i.toDouble}")
  case s: String => println(s"arg is String. length: ${s.length}")
  case b: Boolean => println(s"arg is Boolean. toNOT: ${!b}")
  case _ => println("arg is other type.")
}

scala> patternMatch(9)
arg is Int. toDouble: 9.0

scala> patternMatch("hello")
arg is String. length: 5

scala> patternMatch(true)
arg is Boolean. toNOT: false

ifを使ってマッチする際の条件を指定できます。
変数が必要なければ_: 型名でも可能です。

def patternMatch(arg: Any): Unit = arg match {
  case i: Int if i == 0 => println("arg is Int and zero.")
  case _: Int => println("arg is Int.")
  case _ => println("arg is other type.")
}

scala> patternMatch(0)
arg is Int and zero.

scala> patternMatch(3)
arg is Int.

Listのパターンマッチ

def patternMatch(arg: AnyRef): Unit = arg match {
  case List(1, x, y, z) => println(s"x is ${x}. y is ${y}. z is ${z}.")
  case _ => println("other")
}

scala> patternMatch(List(1,2,3,4))
x is 2. y is 3. z is 4.

scala> patternMatch(List(1,2))
other

コンス(::)でもパターンマッチできます。コンスを利用すれば、Listの要素数に関係なくマッチさせることができます。

def patternMatch(arg: AnyRef): Unit = arg match {
  case List(1, x, y, z) => println(s"x is ${x}. y is ${y}. z is ${z}.")
  case x::y => println(s"head is ${x}. the others are ${y}")
  case _ => println("other")
}

scala> patternMatch(List(1,2,3,4))
x is 2. y is 3. z is 4.

scala> patternMatch(List(1,2,3))
head is 1. the others are List(2, 3)

scala> patternMatch(List(9,8,7,6,5))
head is 9. the others are List(8, 7, 6, 5)

シールドクラスとパターンマッチ

参考サイト
【Scala】列挙型を使おう | Developers.IO

sealedキーワードを付けたシールドクラスは、同一ファイル内に定義されたクラスのみ継承できます。
またシールドクラスは、継承されたクラスでパターンマッチするときに、漏れているものをコンパイルの際に警告してくれます。

sealed abstract class Rank
case object A extends Rank
case object B extends Rank
case object C extends Rank

def patternMatch(rank: Rank): Unit = rank match {
  case A => println("excellent!")
  case B => println("good!")
  case C => println("so so.")
}

scala> patternMatch(B)
good!

// もしcase Cが無いと、
// <console>:14: warning: match may not be exhaustive.
// It would fail on the following input: C
// のような警告が出ます。