今回は パターンマッチ だ!!
こいつ凄い奴なんだぜ。
実は最初、全く使い道がわからなかった。
ifで良いんじゃね?とか思ってけど、
ifよりもやれることの幅が広すぎるんだ。
書きながら見てみよう!
パターンマッチとは
ifやswitchのように条件分岐に分類されるのかな。
ただifやswitchと同等に使うだけではもったいない。
どれだけ凄いか見てみよう!
パターン
意識してなかったけどパターン色々あるんだね。。。
最初は戸惑うかもしれないけど、簡単なパターンから見てみよう。
定数パターン
まずは、Javaのswich文ぽいやつ。
import scala.util.Random
object PatternMatchConst {
def main(args: Array[String]): Unit = {
val random = Random.nextBoolean
val num = random match {
case true => 1
case false => 0
}
println("random:" + random)
println("number:" + num)
}
}
動かしてみるよ!
$ scalac PatternMatchConst.scala
$ scala PatternMatchConst
random:true
number:1
ここではランダムなBoolean
を生成して変数random
に束縛している。
そして変数random
に対してパターンマッチを実施する。
それがcase
の後のtrue
とfalse
だ。
そしてマッチした方の値を変数num
に束縛させる。
パターンマッチも式であることを意識するために
戻り値を返し変数に束縛させるソースコードにしてみた。
束縛とは
本とかだと、以下のような記述が良く出てくる。
値を変数に束縛する
式で得た結果を変数に紐づけるという感じなのかな。
代入と似ているけど、考え方から違うんだよね。
bind の訳として用いられているね。
今回はパターンマッチが本題なので、詳しく知りたい場合は以下を読んでみて下さい。
Smalltalk use: better《余録》代入 vs. 束縛
型パターン
次は 型 だ。Javaのinstanceof
と似ている。
object PatternMatchType {
def main(args: Array[String]): Unit = {
val list = List(1, true, "J")
for (value <- list) {
value match {
case num: Integer => println("num=" + num)
case bool: Boolean => println("bool=" + bool)
case name: String => println("name=" + name)
case _ => println("value=" + value)
}
}
}
}
$ scalac PatternMatchType.scala
$ scala PatternMatchType
num=1
bool=true
name=J
型に応じてやることを決めているんだ。
ワイルドカードパターン
つぎはワイルドカード_
だ。
object PatternMatchWild {
def main(args: Array[String]): Unit = {
val num = 4
num match {
case _ => println("number:" + num)
}
}
}
$ scalac PatternMatchWild.scala
$ scala PatternMatchWild
number:4
全てのパターンにマッチしちゃうんだ。
ワイルドカードパターンがない場合
どうなるか見てみよう。
object PatternMatchNoWild {
def main(args: Array[String]): Unit = {
val list = List(1, true, "J")
for (value <- list) {
value match {
case num: Integer => println("num=" + num)
case bool: Boolean => println("bool=" + bool)
}
}
}
}
$ scalac PatternMatchNoWild.scala
$ scala PatternMatchNoWild
num=1
bool=true
scala.MatchError: J (of class java.lang.String)
at PatternMatch3_2$$anonfun$main$1.apply(PatternMatch3_2.scala:7)
at PatternMatch3_2$$anonfun$main$1.apply(PatternMatch3_2.scala:6)
at scala.collection.immutable.List.foreach(List.scala:318)
at PatternMatch3_2$.main(PatternMatch3_2.scala:6)
at PatternMatch3_2.main(PatternMatch3_2.scala)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at scala.tools.nsc.util.ScalaClassLoader$$anonfun$run$1.apply(ScalaClassLoader.scala:71)
at scala.tools.nsc.util.ScalaClassLoader$class.asContext(ScalaClassLoader.scala:31)
at scala.tools.nsc.util.ScalaClassLoader$URLClassLoader.asContext(ScalaClassLoader.scala:139)
at scala.tools.nsc.util.ScalaClassLoader$class.run(ScalaClassLoader.scala:71)
at scala.tools.nsc.util.ScalaClassLoader$URLClassLoader.run(ScalaClassLoader.scala:139)
at scala.tools.nsc.CommonRunner$class.run(ObjectRunner.scala:28)
at scala.tools.nsc.ObjectRunner$.run(ObjectRunner.scala:45)
at scala.tools.nsc.CommonRunner$class.runAndCatch(ObjectRunner.scala:35)
at scala.tools.nsc.ObjectRunner$.runAndCatch(ObjectRunner.scala:45)
at scala.tools.nsc.MainGenericRunner.runTarget$1(MainGenericRunner.scala:74)
at scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:96)
at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:105)
at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)
こんな感じになっちゃいました。
何かしらのパターンにマッチしないといけないんだね。
別の手段で制御しないのであれば、ワイルドカードパターンを最後に書くようにしよう。
変数パターン
次は変数に束縛させてみるよ。ここからJavaの世界から離れるね。
object PatternMatchVar {
def main(args: Array[String]): Unit = {
val random = 4
val result = random match {
case num => num * 2
}
println("random:" + random)
println("number:" + result)
}
}
$ scalac PatternMatchVar.scala
$ scala PatternMatchVar
random:4
number:8
まずこのパターンは全ての値にマッチするんだ。
しかもマッチした値を __変数 num
に束縛__させているんだ。
そして処理の中で使用している。
変数num
を2倍にしているんだ。
ワイルドカードパターンと似ているけど、別の変数に束縛していところが違うんだ。
##シーケンスパターン
なんとシーケンスともマッチングできるんだ。
object PatternMatchSeq {
def main(args: Array[String]): Unit = {
val seq = Seq(1, 2, 3, 4)
val result = seq match {
case Seq(1, a, _*) => a
case _ => 0
}
println("number:" + result)
}
}
$ scalac PatternMatchSeq.scala
$ scala PatternMatchSeq
number:2
ここでは先頭要素が1
で、要素数2個以上のSeq
という条件でマッチングしている。
_*
は0個以上の任意の要素を表すんだ。
そして、1番目の要素を変数a
に束縛しているんだ。
タプルパターン
タプルでももちろんできるぞ。
object PatternMatchTuple {
def main(args: Array[String]): Unit = {
val tuple = (1, 2, "OK")
val result = tuple match {
case (1, 2, x) => x
case _ => "NG"
}
println("result:" + result)
}
}
$ scalac PatternMatchTuple.scala
$ scala PatternMatchTuple
result:OK
もう馴れてきたかな?
コンストラクタパターン
ケースクラスと組み合わせて使うパターンだ。
フィールドまでマッチングできるようになるんだ。
object PatternMatchConstructor {
case class Person(name: String, age: Int, education: String)
def main(args: Array[String]): Unit = {
val person = Person("椿山 清美", 15, "男塾")
val belongTo = person match {
case Person("独眼鉄", _, "男塾") => "鎮守直廊三人衆 "
case Person("椿山 清美", _, "男塾") => "一号生"
case _ => "一般市民"
}
println("所属:" + belongTo)
}
}
$ scalac PatternMatchConstructor.scala
$ scala PatternMatchConstructor
所属:一号生
フィールドまでパターンマッチ対象にしているから
独眼鉄様にマッチしないのがわかったかな?
今回はここまでだ!
#まとめ
パターンマッチ凄い奴だろ?
こんな凄い奴は使わないと損だぞ。
でも最近は、パターンマッチを書かないで綺麗に書けないかな?
と考えてたりするんだよね。。。
今回も
体で感じてくれたかな?