Scala学习小计 - 什么是模式匹配(pattern-matchin)?

简介

模式匹配(pattern-matching),什么是模式(pattern)?,这里的模式用于描述一个结构的组成。

这个pattern和正则里的pattern相似,不过适用范围更广,可以针对各种类型的数据结构,不像正则表达只是针对字符串。

比如:List("a", _, _*) 也是个pattern,表示第一个元素是 "a" ,后续一个或多个元素的 List

简单解读下:

  • 首先这个模式首先声明它只会匹配 List 类型的数据结构。
  • 其次这个List的第一个元素必须是字符串 "a"
  • 然后通过 _ 占位符来表示,第二位元素必须存在,但是不限制其数据类型、数据内容等等。
  • 最后 _* 表示后续的元素也是类型不限,内容不限。* 表示个数都是0或多个的。

在Scala中会经常应用到这个模式匹配的,它能减少if else的判断,能快速的帮你获取到想要的结果情况,并进行下一步的处理。

下面是Scala中的常用例子:

# 匹配一个List,它由三个元素组成,第一个元素为1,第二个元素为2,第三个元素为3
scala> List(1, 2, 3) match { case List(1, 2, 3) => println("ok") }
ok

# 匹配一个List,它至少由一个元素组成,第一个元素为1
scala> List(1, 2, 3) match { case List(1, _*) => println("ok") }
ok

# 匹配一个List,它由三个元素组成,第一个元素为“a",第二个元素任意类型,第三个元素为"c"
scala> List("a", "b", "c") match{ case List("a", _, "c") => println("ok") }
ok

# 当然模式也不仅仅是表示某种结构的,还可以是常量,或类型,如:
scala> val a = 10
a: Int = 10

# 常量模式,如果a与100相等则匹配成功
scala> a match { case 10 => println("ok") }
ok

# 类型模式,如果a是Int类型就匹配成功
scala> a match { case _:Int => println("ok") }
ok

常量匹配

# 例子中case 了一个常量值,然后去匹配s的值。和if语句的效果很像。
scala> val site = "abc"
scala> site match { case "abc" => print("ok") }
scala> val ABC = "abc"
# 注意:匹配模式这里常量必须以大写字母开头。否则 case abc 的话,会将s的赋值到abc身上,而不是模式匹配。
scala> def m(s: String) = s match { case ABC => println("ok") }
scala> m("ab") # 这里会报异常,因为没有case到当前的情况,导致无法处理
scala> m("abc") # 得到ok

变量匹配

变量模式匹配,没有判断变量值,而是单纯的把 site 给赋值到变量 whateverName 上而已。所以这里一般总会匹配成功。
注意:匹配变量这里必须是小写的字母开头,否则会认为是上面的常量匹配。
Future对象的 Future().flatMap { case XX } 就可以理解为变量匹配。

scala> site match { case aaa => println(aaa) }
abc
scala> List(1, 2) match { case List(x, 2) => println(x) }
1

通配符匹配

通配符用下划线表示:_ ,可以理解成一个特殊的变量或占位符。通配符通常用于代表所不关心的部分,它不像变量模式可以后续的逻辑中使用这个变量。
单纯的通配符模式通常在模式匹配的最后一行出现,case _ => 它可以匹配任何对象,用于处理所有其它匹配不成功的情况。这种会处理一些位置的匹配结果情况,从而减少报出异常的情况。
通配符模式也常和其他模式组合使用:

List(1, 2, 3) match {case List(_, _, 3) => println("ok") }
# 上面的 List(_,_,3) 里用了2个通配符表示第一个和第二个元素,这2个元素可以是任意类型

构造器模式

就是去指定一个变量或者class的构造方法的构造规则,然后筛选出符合该匹配规则的变量(或者实例)

# 定义一个case class
scala> case class Person(name: String, age:Int)
defined class Person
# 通过构造器创建一个实例
scala> Person("hanmeimei", 25)
res20: Person = Person(hanmeimei,25)
# 匹配模式的时候可以直接实例的具体内容
scala> res20 match { case Person(_, 25) => print("ok")}
ok
# 也可以在匹配模式的时候,指定赋值的变量,或者声明变量的类型提供更精确的匹配
scala> res20 match { case Person(name, hisAge:Int) => print(name + "-" + hisAge)}
hanmeimei-25

类型匹配

就是判断对象是否是某种类型(在匹配规则处指定变量的类型,而变量可以指定名称,也可以使用通配符):

scala> "hello" match {case _:String => println("ok") }

isInstanceOf 判断类型的效果一样,需要注意的是Scala匹配泛型时要注意,比如:

def m(a: Any) = a match { 
    case a :List[String] => println("success")
    case _ => println("failed")
} 

如果使用了泛型,它会被擦拭掉,如同Java的做法,所以上面的 List[String] 里的 String 运行时并不能检测
m(List("A"))m(List(2)) 都可以匹配成功。实际上上面的语句编译时就会给出警告,但并不出错。
通常对于泛型直接用通配符替代,上面的写为 case a : List[_] =>

注意:针对带有泛型参数的类型匹配,改为构造器模式可以绕开;
或对泛型参数声明 Manifest/TypeTag 是可以在运行时保持泛型参数类型的信息的。

变量绑定匹配

scala> case class Tree(name:String, tree:Tree)
defined class Tree

scala> Tree("node_1", null)
res26: Tree = Tree(node_1,null)

scala> Tree("root", res26)
res27: Tree = Tree(root,Tree(node_1,null))

scala> res27 match { case Tree(name, Tree(nodeName, _)) => print(s"$name - $nodeName") }
root - node_1
scala> res27 match { case Tree(name, t@Tree(nodeName, _)) => print(s"$name - $nodeName - $t") }
root - node_1 - Tree(node_1,null)

用@符号将匹配到的数据绑定到t变量上,只有匹配成功才会绑定。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值