Scala 专题教程-Case Class和模式匹配(10): 模式无处不在

jerry Scala 2015年11月25日 收藏

Scala程序很多地方都可以使用模式,而不仅仅用在模式匹配(match表达式),本篇给出几种使用模式的情况。
变量定义
任何时候你使用val或var定义变量时,你都可以使用模式定义多个变量,此时你定义元组,分别赋值到不同的变量。

  1. scala> val myTuple=(134,"abc")
  2. myTuple: (Int, String) = (134,abc)
  3.  
  4. scala> var(number,string)= myTuple
  5. number: Int = 134
  6. string: String = abc

这个情况也适应Case classes。 如果你知道某个值的具体形式,你可以利用模式来分解单个项:

  1. scala> val exp=new BinOp("*",Number(5),Number(1))
  2. exp: BinOp = BinOp(*,Number(5.0),Number(1.0))
  3.  
  4. scala> val BinOp(op,left,right)=exp
  5. op: String = *
  6. left: Expr = Number(5.0)
  7. right: Expr = Number(1.0)
  8.  

Case序列定义部分方程
一个Case序列(case squence,也成为可选项)为包含在{}的代码,它可以用在可以使用任何方程字面量的地方。从根本上来说case序列也是一个函数字面量,只是更一般化的函数。通常的函数只有一个入口点和一组参数。一个Case序列可以有多个入口点和多组参数。每个Case可选项都是函数的入口点,而它对于模式为参数定义。其函数体为Case序列的右边部分。
这里给出一个简单的例子:

  1. val withDefault: Option[Int] => Int ={
  2. case Some(x) =>x
  3. case None => 0
  4. }

这个函数定义了两个可选项,第一个选项匹配Some对象,第二个选项匹配None。

  1. scala> withDefault(Some(5))
  2. res0: Int = 5
  3.  
  4. scala> withDefault(None)
  5. res1: Int = 0
  6.  

此外需要注意的是,一个Case序列定义了一个部分函数(partial function),如果你传入一个该函数不支持的参数,代码会给出Runtime异常。比如我们定义下面一个部分函数:

  1. val second: List[Int] => Int = {
  2. case x::y::_ => y
  3. }

返回列表的第二个元素,系统会给出如下警告:

  1. <console>:7: warning: match may not be exhaustive.
  2. It would fail on the following input: List(_)
  3. val second: List[Int] => Int = {
  4. ^
  5. second: List[Int] => Int = <function1>
  6.  

系统警告匹配不完全,比如函数无法匹配List(1),List()等,测试如下:

  1. scala> second(List(1,2))
  2. res7: Int = 2
  3.  
  4. scala> second(List(1))
  5. scala.MatchError: List(1) (of class scala.collection.immutable.$colon$colon)
  6. at $anonfun$1.apply(<console>:7)
  7. at $anonfun$1.apply(<console>:7)
  8.  

如果你需要测试某个部分函数是否定义,你就需要告诉编译器你在使用部分函数。而类型List[Int] => Int代表了所有由列表到整数的变换。而如果我们需要定义由List[int]到int的部分函数,需要使用PartialFunction来定义,例如:

  1. val second:PartialFunction[List[Int],Int] = {
  2. case x::y::_ => y
  3. }

PartialFunction 定义了一个isDefinedAt方法可以用来测试某种类型的部分函数是否定了。例如:

  1. scala> second.isDefinedAt(List(5,6,7))
  2. res0: Boolean = true
  3.  
  4. scala> second.isDefinedAt(List(1))
  5. res1: Boolean = false
  6.  

for表达式中使用模式
你也可以在for表示式中使用模式,比如我们之前定义的

  1. val capitals = Map("France"->"Paris", "Japan"->"Tokyo","China"->"Beijing")

我们使用for表示式来枚举国家和首都

  1. for((country,city) <- capitals)
  2. println("The captical of " + country + " is " + city)
  3. The captical of France is Paris
  4. The captical of Japan is Tokyo
  5. The captical of China is Beijing

这个例子的(county,city)匹配都会成功,因为capitals 的每个元素都是一个二元组。如果某些匹配不成功,则这些元素自动跳过。比如:

  1. val results=List(Some("apple"),None,Some("Orange"))
  2.  
  3. scala> for(Some(fruit) <- results) println (fruit)
  4. apple
  5. Orange