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への参加はこちら