您的位置:首页 > 其它

Scala模式匹配(一)

2015-10-24 12:13 387 查看
下面是将要使用到的代码:

abstract class Expr
case class Var(name: String) extends Expr
case class Number(num: Double) extends Expr
case class UnOp(operator: String, arg: Expr) extends Expr
case class BinOp(operator: String, left: Expr, right: Expr) extends Expr


1.通配模式

通配模式用通配符“_”匹配任意对象。可以作为“全匹配”的备选项,也可以用来忽略对象中你不关心的部分。例如,只检查是否是二元操作符,而不关心操作符的元素:
expr match{
case BinOp(_, _, _) => println(expr + "is a binary operation")
case _ => println("It's something else")
}


2.常量模型

常量模式用作匹配自身,任何字面量都可以用作常量。例如5、true、“hello”,和任何的val或单例对象都可以用作常量。如,单例对象Nil只匹配空列表模式:
val TEST = "test"
def test(x: Any) = x match{
//也可以使用常量名,但常量名必须以大写字母开头,否则就成了变量模式
case TEST => "Get test"
case 5 => "five"
case true => "boolean true"
case "hello" => "string"
case Nil => "a empty list"
case _ => "something else"
}


3.变量模式

变量模式类似于通配符,可以匹配任何对象。不过和通配符不同的是,Scala把变量绑定在匹配的对象上,之后你可以用这个变量来操作对象。例如,将expr对象绑定到变量operate上:
expr match{
case operate => println(operate)
}
注意:对于变量,要求必须是以小写字母开头,否则会把它对待成一个常量。

4.构造器模式

构造器模式使模式匹配变得真正的强大。例如,匹配一个样本类,那么这个模式就是表示首先检查对象是否是该名称的样本类的成员,然后检查对象的构造器参数是否符合额外提供的模式。先看一个简单的一层匹配的例子:
case class Person(firstName: String, lastName: String)
case class Dog(name: String)
expr match{
case Person(first, "Alexander") => s"found an Alexander, first name = $first"
case Dog("Suka") => "found a dog named Suka"
case _ => "Unknown"
}
这些额外的模式意味着Scala模式支持深度匹配。这种模式不仅检查顶层对象是否一致,还会检查对象的内容是否匹配内层的模式。由于额外的模式自身可以形成构造器模式,因此可以使用它们检查到对象内部的任意深度。例如,下面的例子中不仅检查了对象顶层的BinOp对象,还检查了它的第三个构造器参数是Number,以及它的值为数字0,即这一行完成了三层深度的检查:
expr match{
case BinOp("+", e, Number(0) => println("a deep match")
case _ =>
}


5.序列模式

你也可以像匹配样本类那样匹配如List或Array这样的序列类型。如,下面的例子检查以0开始的三个元素的列表:
expr match{
case List(0, _, _) => println("The list has three elements")
case _ =>
}
如果想匹配不定长的序列,如下,匹配由0开,任意长度的列表:
expr match{
case List(0, _*) => println("The list has many elements")
case _ =>
}
最后,在看一个复杂一点的迭代匹配:
object MatchWithList extends App{
val x = List(1, 2, 3)
val y = 1 :: 2 :: 3 :: Nil

//recursive
def listToString(list: List[Any]): String = list match{
case s :: rest => s + " " + listToString(rest)
case Nil => ""
}
println(listToString(x))
println(listToString(y))
println(listToString("Apples" :: "Bananas" :: "Oranges" :: Nil))
}


6.元组模式

如下,匹配任意的3-元组:
expr match{
case (a, b, c) => println(s"3-tuple: $a, $b, $c")
case _ =>
}


7.类型模式

类型模式可以当做类型测试和类型转换的简易替代。相当于isInstanceOf,在Scala中不提倡用isInstanceOf。但是,如果这里用到了泛型,则泛型类型会被擦拭掉,类型擦拭下面会讲到。
def test(x: Any) = x match{
case s: String => s.length
case m: Map[_, _] => m.size
case _ => "other"
}


7.1类型擦拭

Scala和Java的泛型擦除模式,是指类型参数信息没有保留到运行期。但是,数组例外,数组的元素类型与数组值保存在一起。这和编译时类型和运行时类型有关。

执行上面的代码时,解释器会发出“未检查警告”,可以使用“-unchecked”命令行选项重新开始解释器来寻求更多细节。
def test(x: Any) = x match{
case m: Map[String, Int] => m.size
case _ => "other"
}
但是,前面提到了Array数组是一个例外。
def test(x: Any) = x match{
case m: Map[_, _] => "It's a map"
case a: Array[String] => "It's a array"
case _ => "other"
}


8.变量绑定

除了独立的变量模式之外,你还可以对任何其他模式添加变量。只要简单地写上变量名、“@”符,以及这个模式,这种写法创造了变量绑定模式。这种模式的意义在于它能像通常的那样做模式匹配,并且如果匹配成功,则把变量设置成匹配的对象,就像使用简单的变量模式那样。

例如,为寻找一行中使用了两遍绝对值操作符的模式匹配,变量绑定可以使其简化为仅使用一次绝对值操作:
expr match{
case UnOp("abs", e @ UnOp("abs", _)) => e
case _ =>
}
一个用e作为变量及UnOp("abs", _)作为模式的变量绑定模式。如果整个模式匹配成功,那么符合UnOp("abs", _)的部分就可以使用e指代。正如代码中写的,之后e就会保持原样返回。

最后,我们再给一个稍微综合点的例子:
trait Animal
case class Dog(name: String) extends Animal
case class Cat(name: String) extends Animal
case object Woodpecker extends Animal

object MatchClasses extends App{

def determineType(x: Animal): String = x match {
//    case Dog(moniker) => "Got a Dog, name = " + moniker
//or
//变量绑定“@”是要绑定到具体的对象上,针对具体对象或实例
case d @ Dog("Rocky") => "Got a Dog, name = "+ d.name
//类型匹配,针对类或类型
case d: Dog => "Got a Dog, name = "+ d.name
case _: Cat => "Got a Cat (ignoring the name)"
//参考常量模式
case Woodpecker => "That was a Woodpecker"
case _ => "That was something else"
}
println(determineType(Dog("Rocky")))
println(determineType(Dog("Snoopy")))
println(determineType(Cat("Rusty the Cat")))
println(determineType(Woodpecker))
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Scala 模式匹配