Help us understand the problem. What is going on with this article?

第18章:Scalaのパターンマッチ

More than 5 years have passed since last update.

今回は パターンマッチ だ!!
こいつ凄い奴なんだぜ。

実は最初、全く使い道がわからなかった。
ifで良いんじゃね?とか思ってけど、
ifよりもやれることの幅が広すぎるんだ。

書きながら見てみよう!

パターンマッチとは

ifやswitchのように条件分岐に分類されるのかな。
ただifやswitchと同等に使うだけではもったいない。

どれだけ凄いか見てみよう!

パターン

意識してなかったけどパターン色々あるんだね。。。

最初は戸惑うかもしれないけど、簡単なパターンから見てみよう。

定数パターン

まずは、Javaのswich文ぽいやつ。

PatternMatchConst.scala
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 の後のtruefalse だ。

そしてマッチした方の値を変数num に束縛させる。

パターンマッチも式であることを意識するために
戻り値を返し変数に束縛させるソースコードにしてみた。

束縛とは

本とかだと、以下のような記述が良く出てくる。

値を変数に束縛する

式で得た結果を変数に紐づけるという感じなのかな。
代入と似ているけど、考え方から違うんだよね。

bind の訳として用いられているね。

今回はパターンマッチが本題なので、詳しく知りたい場合は以下を読んでみて下さい。

Smalltalk use: better《余録》代入 vs. 束縛

型パターン

次は だ。Javaのinstanceofと似ている。

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

型に応じてやることを決めているんだ。

ワイルドカードパターン

つぎはワイルドカード_だ。

PatternMatchWild.scala
object PatternMatchWild {

  def main(args: Array[String]): Unit = {
    val num = 4

    num match {
      case _ => println("number:" + num)

    }
  }

}
$ scalac PatternMatchWild.scala

$ scala PatternMatchWild
number:4

全てのパターンにマッチしちゃうんだ。

ワイルドカードパターンがない場合

どうなるか見てみよう。

PatternMatchNoWild.scala
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の世界から離れるね。

PatternMatchVar.scala
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倍にしているんだ。

ワイルドカードパターンと似ているけど、別の変数に束縛していところが違うんだ。

シーケンスパターン

なんとシーケンスともマッチングできるんだ。

PatternMatchSeq.scala
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 に束縛しているんだ。

タプルパターン

タプルでももちろんできるぞ。

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

もう馴れてきたかな?

コンストラクタパターン

ケースクラスと組み合わせて使うパターンだ。
フィールドまでマッチングできるようになるんだ。

PatternMatchConstructor.scala
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
所属:一号生

フィールドまでパターンマッチ対象にしているから
独眼鉄様にマッチしないのがわかったかな?

今回はここまでだ!

まとめ

パターンマッチ凄い奴だろ?
こんな凄い奴は使わないと損だぞ。

でも最近は、パターンマッチを書かないで綺麗に書けないかな?
と考えてたりするんだよね。。。

今回も
体で感じてくれたかな?

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away