scalaのパターンマッチについて

今回はscalaのパターンマッチについてメモ
ケースクラスとパターンマッチの組み合わせはだいぶ強力なことができるということで、ちゃんと覚えておくためのブログに残しておきます。

パターンマッチで使うmatch式はこんな感じになります。

<セレクター式> match { <選択肢> }

・match式のマッチング方法
match式では上から順番にマッチするかどうかがチェックされ、どれか1つの条件にマッチした場合、それ以降の条件に対するマッチングは行われません。

パターンマッチのパターンはいくつかありますが、その中からいくつかをまとめてみました。
1,定数パターン
2,変数パターン
3,ワイルドカードパターン
4,コンストラクタパターン
5,型付きパターン

・定数パターン
定数パターンは自分自身としかマッチしません。
ようは決め打ちの値。定数としては任意のリテラルを使えます。(string,boolean,intなど)

scala> def const(x: Any) = x match {
     | case 1 => "number"
     | case true => "true"
     | case "constant" => "String"
     | case Nil => "empty"
     | }
const: (x: Any)String

scala> const(1)
res0: String = number

scala> const("constant")
res1: String = String

scala> const(100)
res2: String = something

・変数パターン

scala> def expr(x: Any) = x match {
     | case 0 => "ZERO"
     | case something => "not ZERO:" + something
     | }
expr: (x: Any)String

//マッチした変数を参照することも可能
scala> expr("hello")
res1: String = not ZERO:hello

コンストラクタパターン
前回のケースクラスについての記事で、ケースクラスに対してもパターンマッチができると書きました。下がその例です。

scala> case class Iphone(color:String, price:Int)
defined class Iphone

scala> def caseClassMatch(p: Iphone) = p match {
     | case Iphone( _, 900)=>{
     | println("price is $900")
     | }
     | case Iphone( "green", _)=>{
     | println("Color is green")
     | }
     | }
caseClassMatch: (p: Iphone)Unit

scala> val myIphone = Iphone("red", 900)
myIphone: Iphone = Iphone(red,900)

scala> caseClassMatch(myIphone)
price is $900

ケースクラスのコンストラクタに対してパターンマッチングされていることが確認できると思います。

*ケースクラスのコンストラクタの引数にさらにケースクラスがある場合も、match式でネストされたケースクラスにもパターンマッチが可能ということ。

型でマッチさせることも可能です。
5,型付きパターン

scala> def patternMatch(n:Any) = n match {
     | case n:Int => println("int")
     | case n:String => println("string")
     | case n:Boolean => println("boolean")
     | }
patternMatch: (n: Any)Unit

scala> patternMatch(1)
int

scala> patternMatch("Good")
string

以上、他の言語よりも柔軟な使い方ができるscalaのパターンマッチでした。


追記

変数を束縛(bind)する方法

scala> case class Iphone(color: String, price: Int)
defined class Iphone

scala> def caseClassMatch(p: Iphone) = p match {
     |   case Iphone(_, 900) => println("900 yen")
     |   case Iphone("green", _) => println("green!")
     |   case Iphone(color, _) => println("color is " + color)
     | }
caseClassMatch: (p: Iphone)Unit

scala> caseClassMatch(Iphone("red", 300))
color is red

変数を束縛するという表現がピンとこなかったけど、
ワイルドカードパターンと比べてみたら理解できたので書いてみる。

ワイルドカードのパターン
任意の条件にマッチした場合、予め決めていた処理を行う。

・変数を束縛する変数パターン
ワイルドカードと同じように任意の条件にマッチする。ここまで一緒。
違うのはこれからで、パターン変数xに値が束縛されて参照が可能になります。
上の式だとcolorに値が束縛され、参照できるようになっています。

パターンがマッチしなかった場合どうなるか?

上の例でどのパターンにもマッチしなかった場合どうなるか?という指摘があったため追記です。
結論から言うとerrorになります。下記参照

scala> case class Iphone(color:String, price:Int)
defined class Iphone

scala> def caseClassMatch(p: Iphone) = p match {
     | case Iphone( _, 900)=>{
     | println("price is $900")
     | }
     | case Iphone( "green", _)=>{
     | println("Color is green")
     | }
     | }
caseClassMatch: (p: Iphone)Unit

scala> val myPhone = Iphone("red", 100)
myPhone: Iphone = Iphone(red,100)

scala> caseClassMatch(myPhone)
scala.MatchError: Iphone(red,100) (of class Iphone)
  at .caseClassMatch(<console>:9)
  ... 32 elided