CLOVER🍀

That was when it all began.

+:と:+でSeqに対してパターンマッチ

Scalaでパターンマッチによる分解で、よく紹介されるのはListだと思います。

例えば、このような変数を定義して

    val xs = List(1, 2, 3)

このようなパターンマッチを行います。

    xs match {
      case 1 :: 2 :: 3 :: Nil => println("matched!!")
      case _ => // ここには来ない
    }

    xs match {
      case one :: rest => println(s"matched!! one[$one], rest[$rest]")
      case _ => // ここには来ない
    }

結果。

matched!!
matched!! one[1], rest[List(2, 3)]

こんな感じのことを、Seqに対しても行うことのできる+:と:+の存在に今日気付きました。
知らなかったー!
*まあ、ListもSeqですが

Scala 2.10で追加されたものらしいです。

なお、これらはSeq(正確にはSeqLikeトレイトをMix-inしたもの)に対してパターンマッチをかけられるもののようですので、MapやArrayに対しては不可ですね。

+:

正確には、scala.collection.+:です。Seqをheadとtailに分解することができます。

ここは、Vectorを使って例を。

    val v = Vector(1, 2, 3)

このようなパターンマッチのコードを用意します。

    // headとtail
    v match {
      case 1 +: 2 +: rest => println(s"matched!! $rest")
      case _ => // ここには来ない
    }

    v match {
      case n +: rest => println(s"matched!! $n +: $rest")
      case _ => // ここには来ない
    }

結果。

matched!! Vector(3)
matched!! 1 +: Vector(2, 3)

ちゃんとできてる!素晴らしい!

ListのNilのようなものはないと思いますが、一応最後の要素が空になってもマッチするみたいです。

    v match {
      case 1 +: 2 +: 3 +: rest => println(s"matched! $rest")
      case _ => // ここには来ない
    }

結果。

matched! Vector()

:+

scala.collection.:+です。Seqをinitとlastに分解します。

こちらも、Vectorで。

    val v = Vector(1, 2, 3)

パターンマッチ。

    // initとlast
    v match {
      case init :+ 2 :+ 3 => println(s"matched!! $init")
      case _ => // ここには来ない
    }

    v match {
      case init :+ n => println(s"matched!! $init :+ $n")
      case _ => // ここには来ない
    }

結果。

matched!! Vector(1)
matched!! Vector(1, 2) :+ 3

こちらも、先頭の要素を空でマッチさせることが可能みたいです。

    v match {
      case init :+ 1 :+ 2 :+ 3 => println(s"matched!! $init")
      case _ => // ここには来ない
    }

結果。

matched!! Vector()

なるほど、こんなのがあったとは。勉強になりました〜。

追記
このブログを公開した後で、Twitterでこのエントリに対してツッコミをもらったのですが、そういえばSeq自体でパターンマッチが可能でした。

    v match {
      case Vector(1, rest @ _*) => println(s"matched!! $rest")
      case _ => // ここには来ない
    }

    v match {
      case Seq(1, 2, rest @ _*) => println(s"matched!! $rest")
      case _ => // ここには来ない
    }

結果。

matched!! Vector(2, 3)
matched!! Vector(3)

どこかで見た記憶はありましたが、完全に忘れてましたね…。