前言

首先。我们要在一開始强调一件非常重要的事:Scala的模式匹配发生在但绝不仅限于发生在match case语句块中。这是Scala模式匹配之所以重要且实用的一个关键因素!我们会在文章的后半部分具体地讨论这一点。

本文原文出处: http://blog.csdn.net/bluishglc/article/details/51056230 严禁不论什么形式的转载。否则将托付CSDN官方维护权益。

模式匹配的种类

在Scala中一共同拥有例如以下几种类型的模式匹配:

  1. 通配符匹配(Wildcard Pattern Matching )

  2. 常量匹配 (Constant Pattern Matching )

  3. 变量匹配(Variable Pattern Matching )

  4. 构造函数匹配(Constructor Pattern Matching )

  5. 集合类型匹配(Sequence Pattern Matching )

  6. 元祖类型匹配(Tuple Pattern Matching )

  7. 类型匹配(Typed Pattern Matching )

一个包罗万象的样例

让我们来看一下差点儿展示了全部类型的模式匹配的样例:

object PatternMatchingDemo {

    case class Person(firstName: String, lastName: String)
case class Dog(name: String) def echoWhatYouGaveMe(x: Any): String = x match {
// constant patterns
case 0 => "zero"
case true => "true"
case "hello" => "you said 'hello'"
case Nil => "an empty List"
// sequence patterns
case List(0, _, _) => "a three-element list with 0 as the first element"
case List(1, _*) => "a list beginning with 1, having any number of elements"
case Vector(1, _*) => "a vector starting with 1, having any number of elements"
// tuples
case (a, b) => s"got $a and $b"
case (a, b, c) => s"got $a, $b, and $c"
// constructor patterns
case Person(first, "Alexander") => s"found an Alexander, first name = $first"
case Dog("Suka") => "found a dog named Suka"
// typed patterns
case s: String => s"you gave me this string: $s"
case i: Int => s"thanks for the int: $i"
case f: Float => s"thanks for the float: $f"
case a: Array[Int] => s"an array of int: ${a.mkString(",")}"
case as: Array[String] => s"an array of strings: ${as.mkString(",")}"
case d: Dog => s"dog: ${d.name}"
case list: List[_] => s"thanks for the List: $list"
case m: Map[_, _] => m.toString
// the default wildcard pattern
case _ => "Unknown"
} def main(args: Array[String]) {
// trigger the constant patterns
println(echoWhatYouGaveMe(0))
println(echoWhatYouGaveMe(true))
println(echoWhatYouGaveMe("hello"))
println(echoWhatYouGaveMe(Nil))
// trigger the sequence patterns
println(echoWhatYouGaveMe(List(0,1,2)))
println(echoWhatYouGaveMe(List(1,2)))
println(echoWhatYouGaveMe(List(1,2,3)))
println(echoWhatYouGaveMe(Vector(1,2,3)))
// trigger the tuple patterns
println(echoWhatYouGaveMe((1,2))) // two element tuple
println(echoWhatYouGaveMe((1,2,3))) // three element tuple
// trigger the constructor patterns
println(echoWhatYouGaveMe(Person("Melissa", "Alexander")))
println(echoWhatYouGaveMe(Dog("Suka")))
// trigger the typed patterns
println(echoWhatYouGaveMe("Hello, world"))
println(echoWhatYouGaveMe(42))
println(echoWhatYouGaveMe(42F))
println(echoWhatYouGaveMe(Array(1,2,3)))
println(echoWhatYouGaveMe(Array("coffee", "apple pie")))
println(echoWhatYouGaveMe(Dog("Fido")))
println(echoWhatYouGaveMe(List("apple", "banana")))
println(echoWhatYouGaveMe(Map(1->"Al", 2->"Alexander")))
// trigger the wildcard pattern
println(echoWhatYouGaveMe("33d"))
}
}

相应的输入例如以下:

zero
true
you said 'hello'
an empty List
a three-element list with 0 as the first element
a list beginning with 1, having any number of elements
a list beginning with 1, having any number of elements
a vector starting with 1, having any number of elements
got 1 and 2
got 1, 2, and 3
found an Alexander, first name = Melissa
found a dog named Suka
you gave me this string: Hello, world
thanks for the int: 42
thanks for the float: 42.0
an array of int: 1,2,3
an array of strings: coffee,apple pie
dog: Fido
thanks for the List: List(apple, banana)
Map(1 -> Al, 2 -> Alexander)
you gave me this string: 33d

上述演示样例中唯一没有展示的是变量模式匹配。

变量模式匹配非常像通配符模式匹配,唯一的差别在于:使用通配符模式匹配时,你不能在case推导符后面使用匹配到的值。可是变量模式匹配给匹配到的值命名了一个变量名,因此你能够在推导符后面使用它。下面这个样例就演示了变量模式匹配:

 scala> def variableMatch(x:Any):String =x match {
| case i:Int => s"This is an Integer: $i"
| case otherValue => s"This is other value: $otherValue" //You can use var: otherValue.
| }
variableMatch: (x: Any)String scala> println(variableMatch(1))
This is an Integer: 1 scala> println(variableMatch(1.0))
This is other value: 1.0 scala> println(variableMatch("SSS"))
This is other value: SSS

模式匹配的附加约束(Guard)

上述7种模式匹配是语法层面上的模式匹配,非常多时候,仅仅有这7种模式匹配是不够的。程序猿须要依据具体的值做更仔细的匹配,这时,我们须要对模式匹配附加很多其它的约束条件,这些约束条件叫做Guard,相应到代码上就是在case后面再加入if语句用于对匹配做更加仔细的描写叙述。

让我们来看一个样例:

scala> def testPatternGuard(x: (Int,Int)):Int = x match {
| case (a,a)=>a*2
| case (a,b)=>a+b
| }
<console>:8: error: a is already defined as value a
case (a,a)=>a*2
^

上述代码的设计初衷是希望通过模式匹配来推断二元元组中的两个值是不是一样。假设是一样的,使用一种计算逻辑,假设不一样则使用还有一个计算逻辑,可是这段代码是不能编译通过的,Scala要求“模式必须是线性的”。也就是说:模式中的变量仅仅能出现一次。(Scala restricts patterns to be linear: a pattern variable may only appear once in a pattern.)在这个样例中寄希望使用一个变量让Scala在编译时帮助你推断两个值是否一值显然是做不到的,所以必定会报错。在这样的场合就是须要使用if语句来限定匹配条件的时候了,下面正确的做法:

scala> def testPatternGuard(x: (Int,Int)):String = x match {
| case (a,b) if a==b =>s"a==b,so, we can calc it as: a*2=${a*2}"
| case (a,b)=>s"a!=b,just calc it as: a+b=${a+b}"
| }
testPatternGuard: (x: (Int, Int))String scala> println(testPatternGuard((1,2)))
a!=b,just calc it as: a+b=3 scala> println(testPatternGuard((1,2)))
a!=b,just calc it as: a+b=3

Sealed Classe与模式匹配

假设一个类被声明为sealed。则除了在定义这个class的文件内你能够创建它的子类之外,其它不论什么地方都不同意一个类去继承这个类。在进行模式匹配时,我们须要时刻留心你的case语句能否cover全部可能的情形。但假设在匹配一个类族特别是子类时,可能会出现无法控制的情况,由于假设类族是能够自由向下派生的话,过去覆盖了各种情形的case语句就可能不再“全面”了。

所以使用sealed class是对模式匹配一种保护。另外。使用sealed class还能够从编译器那边得到一些额外的优点:当你试图针对case继承自sealed class的case类进行模式匹配时,假设漏掉了某个某些case类,编译器在编译时会给一个warning. 所以说:当你想为一模式匹配而创建一个类族时。或者说你的类族将要被广发使用于模式匹配时,你最好考虑将你的类族超类限定为sealed。比方。当你定义这样一组sealed classes时:

sealed 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

假设你写了这样一个模式匹配:

scala> def describe(e: Expr): String = e match {
| case Number(_) => "a number"
| case Var(_) => "a variable"
| }
<console>:12: warning: match may not be exhaustive.
It would fail on the following inputs: BinOp(_, _, _), UnOp(_, _)
def describe(e: Expr): String = e match {
^
describe: (e: Expr)String

编译器就会给你一个warnining。

模式匹配无处不在

上面我们演示的全部模式匹配都是基于match case语句块的,诚如我们在文章一開始就强调的:假设模式匹配仅仅存在于match case语句中,那这项优秀特性的辐射的能量将会大打折扣,Scala正是将模式匹配发扬到编程的方方面面,才使得模式匹配在Scala里真正地大放异彩。

变量定义中的模式匹配

这可能是Scala的模式匹配最吸引人的地方了,在Scala里,每当你定义一个变量时。你能够直接利用模式匹配同一时候为多个变量一次性赋值!

这一特性被广泛使用于从元组,Case类和构造器中提取相应的值赋给多个变量。下面展示了几种常见的演示样例:

从元组中提取变量

scala> val (number,string)=(1,"a")
number: Int = 1
string: String = a scala> println(s"number=$number")
number=1 scala> println(s"string=$string")
string=a

从构造器中提取额变量

scala> case class Person(name:String,age:Int)
defined class Person scala> val Person(name,age)=Person("John",30)
name: String = John
age: Int = 30 scala> println(s"name=$name")
name=John scala> println(s"age=$age")
age=30

一个更常见的样例是在main函数中提取命令行传递过来的參数列表:

def main(args: Array[String]) {
val Array(arg1,agr2)=args
.....
}

case语句块(函数字面量)中的模式匹配

Scala之偏函数Partial Function 一文中我们具体介绍了偏函数。当中提到使用不含match的case语句块能够构建一个偏函数的字面量。这个偏函数具有多个“入口”,每个入口都由一个case描写叙述,这样在调用一个偏函数时。依据传入的參数会匹配到一个case,这个过程也是模式匹配,这样的模式匹配和match case 的模式匹配是非常类似的。

for循环中的模式匹配

假设我们觉得for循环中声明的局部迭代变量就是一个普通变量,那么在for循环中使用的模式匹配实质上就是前面提到的变量定义中使用的模式匹配,来看一个列子:

scala> val capitals = Map("France" -> "Paris", "Japan" -> "Tokyo")
capitals: scala.collection.immutable.Map[String,String] = Map(France -> Paris, Japan -> Tokyo) scala> for ((country, city) <- capitals)
| println("The capital of "+ country +" is "+ city)
The capital of France is Paris
The capital of Japan is Tokyo

更深的理解

为什么我们须要“模式匹配”?

在一次对Martin Odersky的採訪中。Martin Odersky这样解释到:

我们每个人都有复杂的数据。

假设我们坚持严格的面向对象的风格,那么我们并不希望直接訪问数据内部的树状结构。相反。我们希望调用 方法,然后在方法中訪问。假设我们能够这样做,那么我们就再也不须要模式匹配了,由于这些方法已经提供了我们须要的功能。但非常多情况下。对象并不提供我们须要的方法,并且我们无法(或者不愿)向这些对象加入方法。….. 从本质上讲,当你从外部取得具有结构的对象图时,模式匹配就不可缺少。你会在若干情况下遇到这样的现象。

在这面这段论述中,以及Martin Odersky举例讲到的从XML构建一个DOM类型结构,都无不让我联想到我之前写过的文章:一段关于”多态”的沉思。在这篇文章里我所思索的问题。事实上正是应用模式匹配的绝佳场景!

将模式匹配应用于提取一堆值。这么好用,为什么不用呢?

就像某种反向的表达式。

正向的表达式向结果中插入值,反向的表达式却是给定结果,一旦匹配成功。就能反过来从结果中抽取出一大堆值。

Scala之模式匹配(Patterns Matching)的更多相关文章

  1. 【scala】模式匹配

    Scala的模式匹配是通过match表达式从若干可选项中选择,类似Java中的switch. 例子: val firstArg = if(args.length>0) args(0) else ...

  2. 聊聊 scala 的模式匹配

    一. scala 模式匹配(pattern matching) pattern matching 可以说是 scala 中十分强大的一个语言特性,当然这不是 scala 独有的,但这不妨碍它成为 sc ...

  3. learning scala regular expression patterns

    package com.aura.scala.day01 import scala.util.matching.Regex object regularExpressionPatterns { def ...

  4. scala 常用模式匹配类型

    模式匹配的类型 包括: 常量模式 变量模式 构造器模式 序列模式 元组模式 变量绑定模式等. 常量模式匹配 常量模式匹配,就是在模式匹配中匹配常量 objectConstantPattern{ def ...

  5. Scala学习——模式匹配

    scala模式匹配 1.基础match case(类似java里switch case,但功能强大些) object MatchApp { def main(args: Array[String]): ...

  6. Scala的模式匹配

    1.概述 2.程序示例(普通的示例) 3.模式匹配(Array) 4.程序示例(Array) 5.模式匹配(List) 6.程序示例 7.遍历 8.模式匹配(case class) 9.程序示例(传统 ...

  7. 函数式编程之-模式匹配(Pattern matching)

    模式匹配在F#是非常普遍的,用来对某个值进行分支匹配或流程控制. 模式匹配的基本用法 模式匹配通过match...with表达式来完成,一个完整的模式表达式长下面的样子: match [somethi ...

  8. Scala之集合Collection

    概述 Scala的集合类能够从三个维度进行切分: 可变与不可变集合(Immutable and mutable collections) 静态与延迟载入集合 (Eager and delayed ev ...

  9. scala pattern matching

    scala语言的一大重要特性之一就是模式匹配.在我看来,这个怎么看都很像java语言中的switch语句,但是,这个仅仅只是像(因为有case关键字),他们毕竟是不同的东西,switch在java中, ...

随机推荐

  1. http://blog.sina.com.cn/s/blog_62e1faba010147k4.html

    http://blog.sina.com.cn/s/blog_62e1faba010147k4.html

  2. Server 非阻塞

    import socket import select import Queue port =500 host = "" sock = socket.socket(socket.A ...

  3. 原生 javascript 基础回顾

    (1)打开新窗口 语法: window.open([URL], [窗口名称], [参数字符串]) 参数说明: URL:可选参数,在窗口中要显示网页的网址或路径.如果省略这个参数,或者它的值是空 字符串 ...

  4. lodash 检查值是否存在 includes

    _.includes(collection, value, [fromIndex=0]) 检查 值 是否在 集合中,如果集合是字符串,那么检查 值 是否在字符串中. 其他情况用 SameValueZe ...

  5. Photoshop - 描边

    描边在后期的UI制作的时候会比较少用,因为有一些缺陷,可以用选取收缩 1.快捷键E + S  (菜单栏-编辑-描边.photoshop cc) 2.使用图层样式进行描边(双击图层列表区域,图层缩略图的 ...

  6. EXTJS4自学手册——简单图形(circle,rect,text,path)

    一.画圆形: xtype: 'button', text: '画图一个圆', handler: function (btn) { Ext.create('Ext.window.Window', { l ...

  7. node.js零基础详细教程(1):安装+基础概念

    第一章 建议学习时间2小时  课程共10章 学习方式:详细阅读,并手动实现相关代码 学习目标:此教程将教会大家 安装Node.搭建服务器.express.mysql.mongodb.编写后台业务逻辑. ...

  8. 用Darwin开发RTSP级联server(拉模式转发)(附源代码)

    源代码下载地址:https://github.com/EasyDarwin orwww.easydarwin.org 在博客 在Darwin进行实时视频转发的两种模式 中,我们描写叙述了流媒体serv ...

  9. nginx跨域(转2)

    当出现403跨域错误的时候 No 'Access-Control-Allow-Origin' header is present on the requested resource,需要给Nginx服 ...

  10. VS2015 解决方案 或者项目 卡 正在加载 的解决办法

    导致项目无法打开以及VS无法关闭. 解决方法: 1.关闭VS: 2.去C:\Users\<your users name>\AppData\Local\Microsoft\VisualSt ...