Strange type conversion from List[(Char, Char)] to

2019-09-18 02:42发布

问题:

I have a recursive function count in Scala 2.9.2 that looks like this

def count(traces: Seq[(Char, Char)], acc: (TP, TN, FP, FN)): (TP, TN, FP, FN) = {
  val (tp, tn, fp, fn) = acc
  traces match {
    case Nil => acc
    case ('(', '(')::rest => count(rest, (tp + 1, tn, fp, fn))
    case (')', ')')::rest => count(rest, (tp + 1, tn, fp, fn))
    case ('(', ')')::rest => count(rest, (tp, tn + 1, fp, fn))
    // ... exhaustive set of cases ...
  }
}

On input Seq(('(', '(')) the function throws the following MatchError:

scala.MatchError: Vector(((,()) (of class scala.collection.immutable.Vector)

I investigated this by playing around with the code in the Scala console.

scala> val t = Seq(('a', 'b'), ('b', 'c'))
t: Seq[(Char, Char)] = List((a,b), (b,c))

scala> t match { case Nil => "h"; case ('a', 'b')::rest => rest }
res6: java.lang.Object = List((b,c))

scala> t1 match { case Nil => "h"; case ('a', 'b')::rest => rest }
scala.MatchError: List((b,c)) (of class scala.collection.immutable.$colon$colon)

It seems as if matching ('a', 'b')::rest (the second line) doesn't return an object of the correct type, since the Seq[(Char, Char)] is suddenly of type java.lang.Object which Scala then doesn't know how to match on.

What explains this behavior?

回答1:

The problem with your pattern matching is that you use extractors defined only for List class but passing to them Vector.

If you really need to match against every possible Seq, you may want to use general syntax:

   foo match {
     case Seq('(' -> ')',rest@ _* ) => println("The tail is" + rest)
   }

(Don't be confused with -> arrow, 1 -> 2 is essentially the same as (1,2), but in this particular case much more readable: you will not mess usual braces with '(' and ')' like in Seq(('(',')'), ... ))

Otherwise, just strict your argument type to List[(Char, Char)].