Scalaのパターンマッチは変数を使って分割代入ができる。
val xs = 1 :: 2 :: 3 :: Nil xs match { case head :: tail => s"head is $head" // => "head is 1" case _ => "not found" }
これはxsに対してまずhead :: tail という式にマッチが試みられ、headには1が、tailには2 :: 3 :: Nilがマッチするので、それぞれの変数に代入され、後続の式で使えるようになっている。
そしてs"head is $head"という式が評価されるが、ここではheadは1になっているので、このmatch式の返り値は"head is 1"になる。
ちなみに、「この式がマッチするのか?」を判定する部分はどう実装されているのかというとunapplyメソッドで定義されているのだが、ここでは割愛。
さて、パターンマッチに既に定義された変数をマッチさせたいときについて考えよう。例えば次のようなパターンだ:
val head = 42 val ys = 42 :: 43 :: 44 :: Nil ys match { case ... // 先頭要素がheadになっているようなリストを取り出したい }
素朴に書くと、headというパターン変数が分割代入のスコープで作られてしまうので、元々の変数を覆い隠してしまう:
// 本来xsは先頭が42ではないのだが・・・ xs match { case head :: tail => s"head is $head" // 常にマッチしてしまう!!! case _ => "not found" }
headの部分には、既にあるheadが入ってほしいのに・・・
バックティック
こんなときは、バックティック(バッククオートとも)を使うと良い。使いたい変数をバックティックで囲うことで、「これはパターン変数ではないですよ」と知らせることができる。するとこの変数の中身にマッチするようになる。
xs match {
case `head` :: tail => s"head is $head" // xsはマッチしない
case _ => "not found"
}
ys match {
case `head` :: tail => s"head is $head" // ysはマッチする
case _ => "not found"
}
その他の方法
ifを使う方法もあるが冗長になる。
xs match {
case h :: tail if h == head => s"head is $head"
case _ => "not found"
}
仕様
この仕様は言語仕様で明記されている:
2.13の言語仕様のうち8.1.6によれば:
To resolve the syntactic overlap with a variable pattern, a stable identifier pattern may not be a simple name starting with a lower-case letter. However, it is possible to enclose such a variable name in backquotes; then it is treated as a stable identifier pattern.
(拙訳) 変数パターンとの構文的な衝突を避けるため、安定識別子パターン(stable identifier pattern)は、先頭が小文字の単純な名前としては書けないんだ。でも、その名前をバッククォート
...で囲めば、安定識別子パターンとして解釈されるようになるよ。
安定識別子とは言語仕様上の概念なのでここではあまり考えなくてよい。とにかく、小文字の変数を書くとそれはパターン変数に使われてしまうが、バッククオートで回避できるということが書かれている。
3系の仕様でも同様:
出典
この記事は、Discordで来た質問を記事に起こしたもの。
Discordへの参加はこちら