【Scala】パターンマッチングでの "unbound placeholder parameter" エラー
「Scala関数型デザイン&プログラミング」を進めている。今は3章でデータ型を定義しているところなのだが、パターンマッチングを書いたときに凡ミスでエラーを起こしたのでメモ。
sealed trait List[+A] case object Nil extends List[Nothing] case class Cons[+A](head: A, tail: List[A]) extends List[A] object List { def apply[A](as: A*): List[A] = { if (as.isEmpty) Nil else Cons(as.head, apply(as.tail: _*)) } }
このようなList型の定義がある時、このコンパニオンオブジェクトの中に、任意の個数の要素をListの先頭から消す drop
メソッドを追加したい。そこで以下のように書いたところエラーが発生した。
def drop[A](as: List[A], n: Int): List[A] = (as, n) match { case (Nil, _) => Nil // n個消す前に要素が全部消えたらNil case (_, 0) => _ // n個消し終わったら残った要素のListを返す case (Cons(_, xs), i) => drop(xs, i-1) // 先頭を一つ消したリストを使い再帰を行う } // Main.scala val x = List(1, 2, 3, 4, 5) println(List.drop(x, 4)) // unbound placeholder parameter case (_, 0) => _
エラーの原因は、2番目のcaseで返される値が _
になっていること。ついcase(_, 0)
で指定されている _
と同じ変数である感覚で書いてしまったが、 _
はJavaのswitch文で言う default
に相当するものであり、特定の値を格納する変数ではない。 そのため、返される値の _
が何にも束縛されておらず、 unbound placeholder parameter
のエラーとなった。
正しく書くには、条件部分で _
ではなく変数を使い、それをそのまま返す値として渡すようにすれば良い。
def drop[A](as: List[A], n: Int): List[A] = (as, n) match { case (Nil, _) => Nil case (l, 0) => l case (Cons(_, xs), i) => drop(xs, i-1) }
ただし、これだと2番目のcaseは受け取った as
引数をそのまま返しているだけなので冗長になる。このようなcaseは、if文としてパターンマッチングの外に出してしまった方が良い。
def drop[A](as: List[A], n: Int): List[A] = { if (n <= 0) as else as match { case Nil => Nil case Cons(_, xs) => drop(xs, n-1) } }