Scalaのパターンマッチでは
val list = List(1,2,3)
list match {
case x::xs => println(s"the head element is ${x}")
case _ => println("no elements exist")
}
みたいなことができる。
Scala始めた時に「こんな書き方できるよ!」と覚えて以降、便利でいつも使っているのだが、どういう仕組みでマッチできているのかよくわかんなかったのでググったがあまり出てこなくて調べるのが大変だったのでまとめておく。
(自分用の短い)結論
自分の知りたかったところ(知らなかったところ)だけまとめておくと、
case x :: xs
=> のときの ::
は def ::(x: A): List[A]
ではなく、case class ::[B](head: B, tl: List[B])
というのが定義されていて、その:: (x,xs)
を呼び出す糖衣構文であった。
infix-operation-patternsというパターンマッチの構文で、
http://www.scala-lang.org/files/archive/spec/2.12/08-pattern-matching.html#infix-operation-patterns
case文の中で抽出子やコンストラクタのオブジェクト/クラス名の前に第一引数を持ってこれる、というものだった。
段階的な説明
val result = 5-4
result match {
case 1 => println("one")
case _ => println("other")
} // -> one
というように、 a match { case b => c}
で、aの値がbと等しいとき、cを実行する(cの値を返す)ということができる。
case以下には柔軟な記法で色々なマッチができて、
http://qiita.com/techno-tanoC/items/3dd3ed63d161c53f2d89#patterns
こちらの記事などに一覧がある。
caseで指定されるのが、unapply(かunapplySeq)というメソッドがが定義されたオブジェクトであれば、unapplyの返り値でマッチできる。返り値がSome(かtrue)であればマッチしたこととなる。
http://qiita.com/techno-tanoC/items/3dd3ed63d161c53f2d89#unapply
object AAA{
def unapply(aaa:AAA) = Some("bbb","ccc","ddd")
}
val hoge = ???
hoge match{
case AAA(x,y,z) => println(s"x=${x}, y=${y}, z=${z}")
case _ => println("does not match")
} // => x=bbb, y=ccc, z=ddd
のような形で case オブジェクト名(変数名)
のようにしておくとunapplyの返り値を変数に束縛してくれる。
scalaのcase classを定義すると自動的にunapplyメソッドが定義されたコンパニオンオブジェクトが定義されるので、
case class CC(x:String,y:Int)
val hoge = CC("Hello","3")
hoge match{
case CC(x,y) => println(s"x=${x}, y=${y}")
case _ => println("does not match")
} // => x=bbb, y=3
などとできる。
ScalaのListには子case classとしてcase class ::[B](head: B, tl: List[B])
というのがいて、
https://www.scala-lang.org/api/current/scala/collection/immutable/$colon$colon.html
val hoge = List(1,2,3)
hoge match{
case ::(x,xs) => ???
case _ => ???
}
とすれば、xに先頭の要素、xsに残りのリストがバインディングされ束縛される。
さらにcaseの中ではinfix-operation-patternsという糖衣構文があって、
http://www.scala-lang.org/files/archive/spec/2.12/08-pattern-matching.html#infix-operation-patterns
op(p,q)
をp op q
のように記述できる。すると
val hoge = List(1,2,3)
hoge match{
case x :: xs => ???
case _ => ???
}
こういった書き方が可能となる。
感想
私が無知なだけなのですが、こういう infix-operation-patterns とかの仕組みって、どの学習方法で勉強したら身に付けられたんでしょう。Webベースでなくちゃんと書籍とかで勉強したらどこかで勉強できたんですかね…
あるいはScala道を進むには言語仕様くらいちゃんと読まないとだめなのでしょうか…Scalaってこわい