关闭

[Scala]Scala学习笔记十 模式匹配

标签: scala模式匹配matchswitch
389人阅读 评论(0) 收藏 举报
分类:

Scala有一个十分强大的模式匹配机制.可以应用在很多场合:switch语句,类型检查等等.此外Scala还提供了样例类,对模式匹配进行了优化.

1. 更好的switch

如下是Scala中C风格switch语句的等效代码:

var sign = 2
val ch = '+'
ch match {
  case '+' => sign = 1
  case '-' => sign = -1
  case _ => sign = 0
}
println(sign) // 1

与default等效的是捕获所有模式(case _模式).有这样一个捕获所有的模式是有好处的.如果没有模式能匹配并且并没有提供case _,代码会抛出MatchError.

与C语言的switch不同的是,Scala模式匹配不会像C语言那样必须在每个分支的末尾显示使用break语句防止进行下一分支.

与if类似,match也是表达式,而不是语句.前面代码可以简化为:

sign = ch match {
  case '+' => 1
  case '-' => -1
  case _ => 0
}

可以在match表达式中使用任何类型,而不仅仅是数字:

color match{
  case Color.RED => ...
  case Color.BLACK => ...
  ...
}

2. 守卫

假如说我们想扩展上面第一个代码以匹配所有数字.在C风格switch语句中可以通过简单的添加多个case标签来实现:

switch(i){
  case 0:
    ...
    break;
  case 1:
    ...
    break;
  ...
  case 9:
    ...
    break;  
  default:
    ...
}

这如果放在Scala中会更简单,给模式添加守卫(相当于一个判断条件)即可:

ch match{
  case '+' => sign = 1
  case '-' => sign = -1
  case _ if Character.isDigit(ch) => digit = Character.digit(ch, 10)
  case _ => sign = 0
}

守卫可以是任何Boolean条件.

备注

模式总是从上往下进行匹配的.如果守卫的这个模式不能匹配,则捕获所有的模式(`case _`)会被用来尝试进行匹配.

3. 模式中的变量

如果case关键字后面跟着一个变量名,那么匹配的表达式会被赋值给那个变量,进而可以在后面中使用该变量:

val character = '1'
character match {
  case '+' => println("this is +")
  case '-' => println("this is -")
  case ch => println("this is " + ch) // this is 1
}

备注

上面代码中如果给定字符不是'+'或者'-',给定字符则会赋值给变量ch

也可以在守卫中使用变量:

val character = '5'
character match {
  case '+' => println("this is +")
  case '-' => println("this is -")
  case ch if Character.isDigit(ch) => println("this is digit") // this is digit
}

4. 类型模式

可以根据表达式的类型进行匹配:

val str:Any = "Hello World"
str match {
  case s: String => println("this is string " + s) // this is string Hello World
  case x: Int => println("this is integer " + x)
  case ch => println("this is other " + ch)
}

在Scala中,我们更倾向于使用这种的模式匹配,而不是使用isInstanceOf操作符.

备注

注意模式中的变量名.当你在匹配类型的时候,必须给出一个变量名(例如上例中的s,x).否则,将会拿对象本身来进行匹配:
case _: BigInt => Int.MaxValue // 匹配任何类型为BigInt的对象
case BigInt => -1 // 匹配类型为Class的BigInt对象

5. 匹配数组,列表和元组

匹配数组中的内容,可以在模式中使用Array表达式:

def arrayMatch(arr:Array[String]) = arr match {
  case Array("Hello") => println("the array only contain 'Hello'")
  case Array(x,y) => println("the array contain two value " + x + " and " + y)
  case Array(x,_*) => println("the array contain many values " + arr.mkString(","))
  case _ => println("the other array")
}

arrayMatch(Array("Hello")) // the array only contain 'Hello'
arrayMatch(Array("Hello", "World")) // the array contain two value Hello and World
arrayMatch(Array("Hello", "World", "Yoona")) // the array contain many values Hello,World,Yoona

同样也可以使用List表达式(或者使用::操作符)匹配列表:

def listMatch(list:List[String]) = list match {
  case "Hello" :: Nil => println("the list only contain 'Hello'")
  case x :: y :: Nil => println("the list contain two value " + x + " and " + y)
  case "Hello" :: tail => println("the list contain many values " + list)
  case _ => println("the other list")
}

listMatch(List("Hello")) // the list only contain 'Hello'
listMatch(List("Hello", "World")) // the list contain two value Hello and World
listMatch(List("Hello", "World", "Yoona")) // the list contain many values List(Hello, World, Yoona)

同样也可以使用元组表示法匹配元组:

def pairMatch(t:Any) = t match {
  case ("Hello", _) => println("the first value is 'Hello'")
  case (x, "Hello") => println("the first value is " + x + " and the second value is 'Hello'")
  case _ => println("the other tuple")
}

pairMatch(("Hello", "World")) // the first value is 'Hello'
pairMatch(("World", "Hello")) // the first value is World and the second value is 'Hello'
pairMatch(("Hello", "World", "Yoona")) // the other tuple

6. 提取器

前面我们看到模式是如何匹配数组,列表和元组的,这些功能背后是提取器机制,带有从对象中提取值的unapply或unapplySeq方法的对象.unapply方法用于提取固定数量的对象,而unapplySeq提取的是一个序列.

arr match {
  case Array(0, x) => ...
  ...
}

Array伴生对象就是一个提取器,定义了一个unapplySeq方法.该方法被调用时,以被执行匹配动作的表达式作为参数.Array.unapplySeq(arr)产出一个序列的值,即数组中的值.第一个值与零进行比较,二第二个值被赋值给x.

正则表达式是另一个适合使用提取器的场景,如果正则表达式中有分组,可以使用提取器来匹配每个分组:

val pattern = "([0-9]+ ([a-z]+))".r
val str = "99 bottles"
str match {
  // num = 99 item = bottles
  case pattern(num, item) => ...
  ...
}

pattern.unapplySeq("99 bottles")生成一系列匹配分组的字符串,这些字符串分别赋值给变量num和item.

7. 变量声明中的模式

之前我们可以看到模式汇总是可以带变量的,同样我们也可以在变量声明中使用这样的模式:

// x = 1 y = 2
val (x, y) = (1, 2)

这对于使用那些返回对偶的函数是非常有用的:

val (q, r) = BigInt(10) /% 3
println("q value is " + q + " and r value is " + r)
// q value is 3 and r value is 1

备注

/%方法返回包含商和余数的对偶,而这两个值分别被变量q和r捕获到.

8. for表达式中的模式

可以在for推导表达式中使用带变量的模式.对于每一个遍历到的值,使用模式进行变量绑定:

import scala.collection.JavaConversions.propertiesAsScalaMap
for( (key, value) <- System.getProperties() ){
  println(key + " = " + value)
}

在for表达式中,失败的匹配将被跳过(不会抛出异常),下面循环打印出所有值为空白的键,跳过所有其他键:

import scala.collection.JavaConversions.propertiesAsScalaMap
for( (key, "") <- System.getProperties() ){
  println(key + " = ")
}

也可以使用守卫(注意if关键字出现在<-之后):

import scala.collection.JavaConversions.propertiesAsScalaMap
for( (key, value) <- System.getProperties() if value == ""){
  println(key + " = " + value)
}
1
0
查看评论

scala 的模式匹配与类型系统

主要内容: 1. scala模式匹配 2. scala类型系统
  • sinat_25306771
  • sinat_25306771
  • 2016-07-23 15:10
  • 2754

【Scala】模式匹配和样本类

模式匹配要理解模式匹配(pattern-matching),先把这两个单词拆开,先理解什么是模式(pattern),这里所的模式是数据结构上的,这个模式用于描述一个结构的组成。我们很容易联想到“正则表达”里的模式,不错,这个pattern和正则里的pattern相似,不过适用范围更广,可以针对各种类...
  • JasonDing1354
  • JasonDing1354
  • 2015-06-21 15:20
  • 2007

Scala之模式匹配(Patterns Matching)

前言首先,我们要在一开始强调一件很重要的事:Scala的模式匹配发生在但绝不仅限于发生在match case语句块中,这是Scala模式匹配之所以重要且有用的一个关键因素!我们会在文章的后半部分详细地讨论这一点。模式匹配的种类在Scala中一共有如下几种类型的模式匹配: 通配符匹配(Wildcard...
  • bluishglc
  • bluishglc
  • 2016-04-04 13:44
  • 8946

scala模式匹配和正则表达式

匹配字面量和常量
  • fanshadoop
  • fanshadoop
  • 2014-06-26 10:37
  • 5890

Scala School 笔记(三)--模式匹配与函数组合

函数组合 让我们创建两个函数: scala> def f(s: String) = "f(" + s + ")" f: (String)java.lang.String scala> def g(s: String) = "g(&qu...
  • li385805776
  • li385805776
  • 2014-02-14 15:01
  • 4996

Scala教程(二十)For循环模式匹配与表达式

For循环模式匹配与表达式; 1.1 模式匹配下的For循环; 1.2 模式匹配下的赋值语句; 1.3 模式匹配下的提取器; 1.4 Scala中For表达式; 1.5 For表达式查询过滤;
  • yuan_xw
  • yuan_xw
  • 2015-11-11 17:39
  • 1239

scala 模式匹配与case class

当一个类被声明为case class时,编译器会自动进行如下操作: 1.构造器中参数如果没有被声明为var,则默认为val类型; 2.自动创建伴生对象,同时在伴生对象中实现apply方法,这样在使用时就不用显式地使用new对象; 3.伴生对象中同样可以实现unapply(),从而可以将case...
  • u013063153
  • u013063153
  • 2016-12-19 16:19
  • 474

Scala 模式匹配下的赋值语句

Scala 模式匹配下的赋值语句示例。
  • yyywyr
  • yyywyr
  • 2016-01-07 22:45
  • 703

scala学习:模式匹配高级实战:嵌套的Case class

最近不清楚是工作忙的情况还是知识点越来越深入了。对知识的理解有点吃力起来。到目前已经好几章的视频都是模棱两可的。 先记录这节视频的内容吧。留待以后再深入理解吧。 示例代码: package com.mk.hello abstract class Item case ...
  • mzygssk
  • mzygssk
  • 2015-08-12 22:47
  • 367

scala 模式匹配之Type、Array、List和Tuple

Scala 模式匹配之Type、Array、List和Tuple介绍。
  • yyywyr
  • yyywyr
  • 2015-12-14 20:20
  • 1334
    个人资料
    • 访问:1517566次
    • 积分:22609
    • 等级:
    • 排名:第378名
    • 原创:624篇
    • 转载:133篇
    • 译文:60篇
    • 评论:189条
    博客专栏
    文章分类
    最新评论