关于“::”案例类,我有两个问题。
:: 可以用作
case head :: tail => ...
它是如何工作的?意思是,Scala 用来将 List 实例与 :: case 类匹配的流程到底是什么?假设我有一个 MyClass 类,带有运算符 op,我可以创建一个名为 op 的案例类,我可以将其用作:
case foo op bar => ....
?
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
在Programming in Scala的第 301 页,关于List
s 上的模式匹配中有详细说明。
“cons”模式
x :: xs
是中缀操作模式的一个特例。您已经知道,当被视为表达式时,中缀操作等同于方法调用。对于模式,规则是不同的:当被视为模式时,中缀操作p op q
等价于op(p, q)
. 也就是说,中缀运算符op
被视为构造函数模式。特别是,像这样的 cons 模式x :: xs
被视为::(x, xs)
. 这暗示应该有一个::
与模式构造函数相对应的类。确实有这样的一类。它被命名scala.::
并且正是构建非空列表的类。
实际上, :: 是一个案例类这一事实只是答案的一半。这在模式匹配中起作用的原因是对象 :: 有一个提取器,它是在定义案例类时自动生成的。方便的是, ::.unapply 返回一个列表,因为 :: 扩展了列表。但是,如果您想对 List 使用相同的技巧,您将无法扩展 List,因为它是final。您可以做的是使用适当的 unapply 方法定义一个对象,该方法具有预期的返回签名。例如,要匹配列表的最后一个元素,您可以执行以下操作:
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!"
}
提取器必须返回一个选项,其中包含两个解构元素的元组。