问题
I have two questions regarding the '::' case class.
:: can be used as
case head :: tail => ...
How does it work? Meaning, what is exactly the flow that Scala uses to match a List instance with the :: case class? Given that I have a class MyClass, with operator op, can I create a case class named op that I can use as:
case foo op bar => ....
?
回答1:
scala> abstract class Stack {
| def push(n :Int):Stack
| }
defined class Stack
scala> final case class push(st :Stack,hd :Int) extends Stack {
| override def push(n :Int):Stack = new push(this,n)
| }
defined class push
scala> object NullStack extends Stack {
| override def push(n :Int):Stack = new push(null,n)
| }
defined module NullStack
scala> val s = NullStack.push(1).push(2)
s: Stack = push(push(null,1),2)
scala> def test(s :Stack) = s match { case st push i => println(st +"push " + i) }
test: (Stack)Unit
scala> test(s)
push(null,1)push 2
回答2:
It's detailed in page 301 of Programming in Scala, About pattern matching on List
s.
The "cons" pattern
x :: xs
is a special case of an infix operation pattern. You know already that, when seen as an expression, an infix operation is equivalent to a method call. For patterns, the rules are different: When seen as a pattern, an infix operation such asp op q
is equivalent toop(p, q)
. That is, the infix operatorop
is treated as a constructor pattern. In particular, a cons pattern such asx :: xs
is treated as::(x, xs)
. This hints that there should be a class named::
that correspond to the pattern constructor. Indeed there is such a class. It is namedscala.::
and is exactly the class that builds non-empty lists.
回答3:
Actually, the fact that :: is a case class is only half of the answer. The reason this works in pattern matching is that there is an extractor for object ::, which is generated automatically when a case class is defined. Conveniently, ::.unapply returns a List, because :: extends List. If you want to use the same trick for Lists, though, you won't be able to extend List, because it's final. What you can do is define an object with the appropriate unapply method, which has the expected return signature. For instance, to match the last element of a list, you can do:
object ::> {def unapply[A] (l: List[A]) = Some( (l.init, l.last) )}
List(1, 2, 3) match {
case _ ::> last => println(last)
}
(1 to 9).toList match {
case List(1, 2, 3, 4, 5, 6, 7, 8) ::> 9 => "woah!"
}
(1 to 9).toList match {
case List(1, 2, 3, 4, 5, 6, 7) ::> 8 ::> 9 => "w00t!"
}
The extractor must return an Option, which contains a tuple of the two deconstructed elements.
回答4:
The text cited by eed3si9n is on p. 331 in the PDF-edition of "Programming in Scala" (1st ed.)
来源:https://stackoverflow.com/questions/1521011/scala-case-classes-questions