match式


match式はJavaやCで言うswitch-caseと同じこともできる別ものです。

match式の戻り値はマッチしたパターンの=>右辺の式を評価したものとなります。

match式の基本構文は次の通りです。

マッチ対象の式 match {

case パターン1 [if ガード1] => 式1

case パターン2 [if ガード2] => 式2

case ...

case パターンN => 式N

}

// switchのように使うとこのようになります
val hal = "hal"
val whoAreYou = hal match {
  case "mode" => "no"
  case "ikou" => "no"
  case "hal" => "yes"
}

// 任意の数値も扱えます。
val one = 1
val num = one match {
  case 1 => "one"
  case 2 => "two"
  case _ => "other"
}
// パターンにある_(アンダースコア)はワイルドカードです。このパターンをワイルドパターンとよびます。
// これはswitchで言うdefaultのようなものです。ワイルドパターンはすべてのケースにマッチします。
// フォールスルーは起こりません。よってbreakは必要ありません。

複数のパターンをまとめることもできます。

one match {
  case 1 | 2 =>
    println("first")
    println("second")
  case _ => println("other")
}
// oneが1もしくは2の場合はfirestが表示されます。

switch-case以外の使い方としてコレクション要素の一部にマッチさせる使い方があります。

val lst = List("A", "B", "C", "D", "E")
lst match {
  case List(a, "B", c, d, e) =>
    println("a = " + a)
    println("c = " + c)
    println("d = " + d)
    println("e = " + e)
  case _ => println("nothing")
  // ”Listの2番目の要素がBかつ要素数が5個”のパターンにマッチすると、a, c, d, eに変数lstの2番目を覗いた残りの要素が束縛され=>の右辺の式が評価されます。
}

パターンマッチではガード式が使えます。 ガード式にもマッチしなければ=>の右辺の式は評価されません

lst match {
  case List("A", b, c, d, e) if b != "B" =>
    println("b = " + b)
    println("c = " + c)
    println("d = " + d)
    println("e = " + e)
  case _ =>
    println("nothing")
  // ガード式はBoolean型でなければなりません
  // Listの先頭がAかつ変数bに束縛された値がBでない場合のみマッチします。
}

パターンマッチのパターンはネストされたものも対象にできます。

case class Game(title: String, price: Int, type: String)

val rockman = new Game("rockman", 4900, "ACT" )
val gradius = new Game("gradius", 3200, "STG")
val wizardry = new Game("wizardry", 5200, "RPG")
// Mapは連想配列(HashMap)を作成しています。
for(item = Map(1 -> rockman, 2 -> gradius, 3 -> wizardry) {
  item match {
    case p @ (Game(_, _, "RPG") ) => printf("%s is hack and slash\n", p)
    case p @ (Game(_, _, _) ) => printf("%s is Other"\n, p)
  }
}
// パターンの前の@は”asパターン”と呼ばれるものです。@のあとに続くパターンにマッチする式を@の前の変数に束縛します。
// |(複数マッチ)を使ったパターンマッチの場合は値を取り出すことができません。

値が特定の型に所属する場合のみマッチするパターンも使うことができます。

val obj: AnyRef = "String Literal"
obj match {
  case v: java.lang.Integer =>
    println("Integer")
  case v: String =>
    println(v.toUpperCase(Locale.ENGLISH) )
  // AnyRef型はJavaのObject型に相当する型で、あらゆる参照型の値をAnyRef型の変数に格納することができます。
  // 型でマッチした値は、その型にキャストしたのと同じように扱うことができます
  // しばしば、Scalaではキャストの代わりにパターンマッチが用いられることがあります。
  // 型のパターンマッチではScalaを実行するJVMの制約により、型変数パラメータを使った場合に正しくパターンマッチが行われません。
  // 型変数パラメータを含む型のパターンマッチはワイルドカードパターンを使うよ良いです。
}

toUpperCaseの引数であるLocale.ENGLISHはアッパーケースに変換する際の変換先の言語を指定しています。

引数を指定しない場合、変換先の言語はユーザの言語設定に依存します。 依存を回避するためにできれば指定しましょう