それが僕には楽しかったんです。

僕と MySQL と時々 MariaDB

全てのプログラマに捧げるScala入門 パターンマッチ

はじめに

前回はコレクションについてさらっと触れたが今回は、パターンマッチについて触れる。
しかし、例によってパターンマッチは用途がかなり多いのでここでは基礎的なことにのみ限定して紹介する。

パターンマッチ

match文というのがScalaには存在する。
例えば、次のコードを例に考えてみよう。

scala> def judge(n: Int): String = n match {
     |     case 1 => "one"
     |     case 2 => "two"
     |     case _ => "error"
     | }
judge: (n: Int)String

scala> (1 to 5).toList.foreach( num => println(judge(num)) )
one
two
error
error
error

match文は直前に渡される変数に束縛され実行する。実際に今回はnという変数に束縛されている。
今回は定数パターンでのみ分類したが最初の2つがnが1 or 2のとき最後のものがそれ以外の時を指す。

この程度であれば、if式などを使った方が断然早く楽に感じる。
しかし、このパターンマッチは単に値を格納した変数だけでなく型やその他もろもろのものに対して適用できるのでif式などよりも汎用性が高い。

これ以降はほかにどんな状況が想定できるか解説していく。

型パターン

型についてもパターンマッチを適用できる。

scala> val list = List(1, "hello", true, 1.0)
list: List[Any] = List(1, hello, true, 1.0)

scala> list.foreach(value => value match {
     |     case number: Int => println(number)
     |     case str: String => println(str)
     |     case bool: Boolean => println(bool)
     |     case _ => println("error")
     | })
1
hello
true
error

このように、case後の変数名後にデータ型を書くことができて
それについて処理を分けることもできる。

ただし、最後のcaseで指定しているワイルドカードパターンがなければ
リストの末尾にある、Double型の変数がマッチしないためにエラーが発生する。
つまりパターンマッチは、なにかしらのパターンにマッチさせなければいけないということになる。

変数パターン

これは先程のものと同じかと思うが少し違う。

scala> val num = 2
num: Int = 2

scala> val res = num match {
     | case num => num * 2
     | }
res: Int = 4

これは計算できるかぎりの全てのパターンにマッチする。
そしてマッチしたものは変数に束縛させて処理を行っている。

シーケンスパターン

お次はシーケンス型のパターンにマッチするものである。

scala> val list = List(1,2,3,4)
list: List[Int] = List(1, 2, 3, 4)

scala> val res = list match {
     | case List(1, a, _*) => a
     | case _ => -1
     | }
res: Int = 2

先頭要素が1で要素が2個以上のシーケンス型のコレクションにマッチするものに成る。

タプルパターン

これは個人的に、ネットで情報を探していて一番おもしろかったので紹介する。
有名なFizzBuzzを解くプログラムを例にする。これはタプルを上手に使った面白い解法だった。

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

    (1 to 30).foreach( num => println(judge(num)) )

  }

  def judge(n: Int): String = (n%3, n%5) match {
    case (0, 0) => "FizzBuzz"
    case (0, _) => "Fizz"
    case (_, 0) => "Buzz"
    case _ => n.toString
  }
}

割ったあまりをタプルとして格納して、それを元にマッチさせている非常にスマートな解法だった。

パターンガード

これは定数パターンに通じるものだが、定数や変数を扱う際にそのマッチに更に条件をつけることができるものである。

scala> val n: Int = 5
n: Int = 5

scala> val res = n match {
     |     case n if n%2 == 0 => true
     |     case n if n%2 != 0 => false
     | }
res: Boolean = false

おわりに

この他にもまだコンストラクタパターンなどがあるが実際にライブラリ等を使用した際によく使うものであるので
興味があれば他のパターンマッチについても学習して欲しい