Scala Pattern Matching
在 scala里对pattern有明确的定义,在形式上有以下几种pattern:
1) 常量模式(constant patterns) 包含常量变量和常量字面量
如下所示的scala 脚本,就是一个常量模式匹配
val message = "hello world"
message match {
case "hello world" => println("ok")
}
运行,
C:\WorkSpace6-scala\scala-train\src\com\usoft>scala pattern-match.scala
ok
2) 变量模式(variable patterns)
确切的说单纯的变量模式没有匹配判断的过程,只是把传入的对象给起了一个新的变量名。
val message = "hello world"
message match {
case whateverName => println(whateverName)
}
上面把要匹配的 site对象用 whateverName 变量名代替,所以它总会匹配成功。不过这里有个约定,对于变量,要求必须是以小写字母开头,否则会把它对待成一个常量变量,比如上面的whateverName 如果写成WhateverName就会去找这个WhateverName的变量,如果找到则比较相等性,找不到则出错。
变量模式通常不会单独使用,而是在多种模式组合时使用,比如
List(1,2) match{ case List(x,2) => println(x) }
里面的x就是对匹配到的第一个元素用变量x标记。
3) 通配符模式(wildcard patterns)
通配符用下划线表示:"_" ,可以理解成一个特殊的变量或占位符。
单纯的通配符模式通常在模式匹配的最后一行出现,case _ => 它可以匹配任何对象,用于处理所有其它匹配不成功的情况。
通配符模式也常和其他模式组合使用:
scala> List(1,2,3) match{ case List(_,_,3) => println("ok") }
上面的 List(_,_,3) 里用了2个通配符表示第一个和第二个元素,这2个元素可以是任意类型
通配符通常用于代表所不关心的部分,它不像变量模式可以后续的逻辑中使用这个变量。
4) 构造器模式(constructor patterns)
这个是真正能体现模式匹配威力的一个模式!
我们来定义一个二叉树:
//抽象节点
trait Node
//具体的节点实现,有两个子节点
case class TreeNode(v: String, left: Node, right: Node) extends Node
//Tree,构造参数是根节点
case class Tree(root: TreeNode)
val tree = Tree(TreeNode("root", TreeNode("left", null, null), TreeNode("right", null, null)))
tree.root match {
case TreeNode(_, TreeNode("left", _, _), TreeNode("right", null, null)) => println("ok ok ok")
}
如果我们期望一个树的构成是根节点的左子节点值为”left”,右子节点值为”right”并且右子节点没有子节点
那么可以用下面的方式匹配:
tree.root match {
case TreeNode(_, TreeNode("left", _, _), TreeNode("right", null, null)) =>
println("bingo")
}
5) 类型模式(type patterns)
类型模式很简单,就是判断对象是否是某种类型:
"hello world" match {
case _: String => println("ok")
}
跟 isInstanceOf 判断类型的效果一样,需要注意的是scala匹配泛型时要注意,
比如
def foo(a: Any) = a match {
case a: List[String] => println("ok");
case _ =>
}
如果使用了泛型,它会被擦拭掉,如同java的做法,所以上面的 List[String] 里的String运行时并不能检测
foo(List("A")) 和 foo(List(2)) 都可以匹配成功。实际上上面的语句编译时就会给出警告,但并不出错。
通常对于泛型直接用通配符替代,上面的写为 case a : List[_] => …
6) 变量绑定模式 (variable binding patterns)
这个和前边的变量模式有什么不同?看一下代码就清楚了:
依然是上面的TreeNode,如果我们希望匹配到左边节点值为”left”就返回这个节点的话:
tree.root match {
case TreeNode(_, leftNode@TreeNode("left", _, _), _) => leftNode
}
用@符号绑定 leftNode变量到匹配到的左节点上,只有匹配成功才会绑定