はじめに
N予備校で「Scala基礎」を受講しています。
今回はパターンマッチについてです。
パターンマッチ
def intToName(num: Int): String = {
num match {
case 1 => "one"
case 2 => "two"
case _ => "other"
}
}
これがmatch
の基本。
JSと違ってbreak
を書かなくても次の処理が実行されることはない。
同じ処理結果なら|
でまとめられる。
creature match {
case "cat" | "dog" => "animal"
}
コレクションのパターンマッチ
val seq = Seq("A", "B", "C", "D", "E")
seq match {
case Seq("A", b, c, d, e) =>
println(s"b = ${b}")
println(s"c = ${c}")
println(s"d = ${d}")
println(s"e = ${e}")
case _ =>
println("nothing")
}
一つでもマッチすれば右辺が評価される。
引数や定数などの名前に対して特定の値を固定することを束縛という。
ここでは、Seq
の先頭が"A"で 5つの要素のパターンにマッチすると、残りの b, c, d, e の変数にそれぞれ Seq
の 2番目以降の要素が「束縛」される。
ガード句も使える
val seq = Seq("A", "B", "C", "D", "E")
seq match {
case Seq("A", b, c, d, e) if b != "B" =>
println("b = " + b)
println("c = " + c)
println("d = " + d)
println("e = " + e)
case _ =>
println("nothing")
}
束縛された結果、b = "B"
になるが、if
の条件によってワイルドカードパターンにマッチする。
as パターン
val seq = Seq(Seq("A"), Seq("B", "C", "D", "E"))
seq match {
case Seq(a@Seq("A"), x) =>
println(a)
println(x)
case _ =>
println("nothing")
}
パターンの前に@
がついたものを asパターン
という。
Seq("A")
が 変数a
に入る。
ジェネリクス
def patternMatchGenerics(): Unit = {
val obj: AnyRef = Seq("a", "b", "c")
obj match {
case v: Seq[Int] => println("Seq[Int]")
case v: Seq[String] => println("Seq[String]")
}
}
AnyRef
は JavaのObject
型に対応したデータ型。
ジェネリクスは、コレクション型の[]
の中に入る要素の型指定のこと。
Scala が動いている JVM の仕様で、型として利用されるときには Int
のジェネリクスの型の 情報が消えてしまうため、Seq[Int]
ではなくSeq
としてマッチしてしまう。
case v: Seq[_] => println("Seq[_]")
case _ => println("nothing")
ワイルドカードを利用する。
まとめ
次の更新から関数型プログラミングに入るんですが、match
を使う場面がとにかく多いので整理がてらまとめました。抽象化しようとすると、if
よりもmatch
の方が都合がいいのかと、なんとなく思ってます。