LoginSignup
14
9

More than 5 years have passed since last update.

「case x::xs =>」でなぜリストがマッチできるのか(infix-operation-patterns)

Last updated at Posted at 2017-03-22

Scalaのパターンマッチでは

sample.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文の中で抽出子やコンストラクタのオブジェクト/クラス名の前に第一引数を持ってこれる、というものだった。

段階的な説明

値マッチ.scala
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.scala
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メソッドが定義されたコンパニオンオブジェクトが定義されるので、

caseclass.scala
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

coloncolon.scala
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 のように記述できる。すると

infix.scala
val hoge = List(1,2,3)
hoge match{
  case x :: xs => ???
  case _ => ???
}

こういった書き方が可能となる。

感想

私が無知なだけなのですが、こういう infix-operation-patterns とかの仕組みって、どの学習方法で勉強したら身に付けられたんでしょう。Webベースでなくちゃんと書籍とかで勉強したらどこかで勉強できたんですかね…
あるいはScala道を進むには言語仕様くらいちゃんと読まないとだめなのでしょうか…Scalaってこわい

14
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
9