Spark样本类与模式匹配

一、前言

样本类(case class)与模式匹配(pattern matching)是Scala中一个比较复杂的概念,往往让人感觉深陷泥沼。我在这里对Scala中的样本类与模式匹配进行了一些整理,希望让大家有些收获。

要学习样本类与模式匹配,先要理解这两个基本概念 。样本类是Scala提出的新概念,简单可以理解成希望用来做模式匹配的类加上case关键词就是样本类。模式匹配可以拆开来理解。这里模式不同于设计模式的模式,而是数据结构上的模式。这里的模式(pattern)是一个只包含变量、 有点类似于Java正则表达式中的模式,不过正则中模式只是用于匹配字符串而已,而这里的模式是针对某种类型或数据结构进行抽象出的表达式。

Scala的模式匹配机制,可以应用在switch语句、类型检查以及”析构“等场合。样本类是对模式匹配进行了优化。

二、样本类

样本类与普通类的区别除了在前面添加case关键词之外,Scala还自动给样本类添加了一些方法。下面通过例子进行讲解。


1

2

3

4

5

abstract class Expr

case class FibonacciExpr(n: Int) extends Expr {             // 样本类

  require(n >= 0)

}

首先,样本类会添加与类名一致的工厂方法,可以使用该工厂方法创建对象


1

2

scala> val fb = FibonacciExpr(12)

fb: FibonacciExpr = FibonacciExpr(12)

其次,样本类参数类表中的参数获得val前缀,所以它被当作字段维护


1

2

scala> fb.n

res0: Int = 12

最后,编译器为样本类添加了hashcode,equals和toString等方法的“自然”实现


1

2

scala> fb.toString()

res2: String = FibonacciExpr(12)

三、模式匹配

更好的switch


1

2

3

4

5

ch match {

  case ‘+‘ => sign = 1

  case ‘-‘ => sign = -1

  case _ => sign = 0

}

上面的代码中为match表达式,"case _"对应于Java的default情况,match对应与Java中switch,不过位置与Java不同

Scala: 选择器 match {备选项}            Java: switch (选择器) {备选器}

一个模式匹配中包含一系类的备选项,而每个备选项开始于case关键词 ,并且都包含一个模式(pattern)及一到多个表达式。Java风格的switch能够自然地表达为match表达式,  但两者还是有一定区别。首先,match是Scala的表达式,不是语句,即它始终以值作为返回结果。其次,Scala的备选项表达式永远进行下一个case,无需break。第三、如果没有模式匹配,会跑出MatchError异常,所以必须考虑到所有的情况。

模式的种类

我们已经知道模式是一种针对每种类型或数据结构抽象出表达式,但是模式有多种类型。

通配模式与常量模式


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

def describe(x: Any) = x match {

    case 5 => "five"

    case true => "truth"

    case "hello" => "hi!"

    case Nil => "the empyt list"

    case _ => "somethint else"

}

// 运行结果

scala> describe(5)

res3: String = five

scala> describe(true)

res4: String = truth

scala> describe("hello")

res5: String = hi!

scala> describe(3.23)

res6: String = somethint else

常量模式仅匹配自身 。任何字面量都可以作为常量,例如:5,true,”hello“都是常量模式。任何val变量或单例对象也可作为常量。而通配模式"_"可以匹配任意对象,经常作为最后的匹配或用来忽略对象的类型。

变量模式

单纯的变量模式没有匹配判断的过程,只是为将传入的对象重命名新的变量名 然后在匹配完成后中表达式中使用该传入的对象


1

scala> site match { case whateverName => println(whateverName) }

上面的例子中匹配的site对象用whateverName进行指代,但是变量名有命名规则: 用小写字母开始的名称为变量,否则认为是常量  。

然而变量模式不会单独使用,而是在多种模式中组合使用,如下所示


1

List(1,2) match{ case List(x,2) => println(x) }

上面的例子中x就是将匹配到的第一个元素用变量x标记

构造器模式

构造器模式是真正能体现模式匹配强大的地方!构造器模式能够支持深度匹配(deep match),这是它强大的原因,匹配模式不仅匹配外层的构造器,而且匹配构造器参数是否匹配,这样的话,可以检查到构造器对象内部的任意深度 。实例如下


1

2

3

4

5

6

7

scala> :paste

//抽象节点

trait Node

//具体的节点实现,有两个子节点

case class TreeNode(v:String, left:Node, right:Node) extends Node

//Tree,构造参数是根节点

case class Tree(root:TreeNode)

构造一个根节点包含2个子节点的树


1

scala>val tree = Tree(TreeNode("root",TreeNode("left",null,null),TreeNode("right",null,null)))

如果我们期望一个树的构成是根节点的左子节点值为”left”,右子节点值为”right”并且右子节点没有子节点
那么可以用下面的方式匹配


1

2

3

4

scala> tree.root match {

        case TreeNode(_, TreeNode("left",_,_), TreeNode("right",null,null)) =>

             println("bingo")

    }

在上面的匹配中,模式检查顶层对象TreeNode,以及第二次对象TreeNode("left",_,_), TreeNode("right",null,null),_,和最后第三层对象的参数值”left“,"right","_",这个模式仅有一行但却能检查三层深度。

类型模式

类型模式,就是判断对象的类型,相较于isInstanceOf而言类型模式更加优秀。


1

2

3

4

5

6

obj match {

  case x: Int => x

  case s: String => Integer.parseInt(s)

  case _: BigInt => Int.MaxValue

  case _ => 0

}

类型匹配是发生在运行期的,因此JVM中泛型的类型信息会被擦除,所以不能匹配泛型的特定类型


1

2

case m: Map[String, Int] => ...  // 不行

case m: Map[_, _] => ...  // 匹配通用的Map,OK

但对于数组而言,因为在Java与Scala中数组被特殊处理了,数组的元素类型与数组值保存在一起,因此它可以做模式匹配


1

case a: Array[String] => "yes"

抽取器模式(extractor pattern)

抽取器模式使用unapply来提取固定数量的对象,使用unapplySeq来提取一个序列。先看一个例子


1

2

3

4

5

6

arr match {

  case Array(0) => "0"  // 匹配包含0的数组

  case Array(x, y) => x + " " + y  // 匹配任何带有两个元素的数组,并将元素绑定到x和y

  case Array(0, _*) => "0 ..."  // 匹配任何以0开始的数组

  case _ => "something else"

}

在前面的代码 case Array(0, x) => ...中, Array(0, x)部分实际上是使用了伴生对象中的提取器,实际调用形式是: Array.unapplySeq(arr)。根据Javadoc,提取器方法接受一个Array参数,返回一个Option。

使用抽取器模式能够数组、列表和元组,还可以使用在正则表达式


1

2

3

4

val pattern = "([0-9]+) ([a-z]+)".r

"99 bottles" match {

  case pattern(num, item) => ...

}

变量绑定模式

变量绑定模式可以对其它模式添加变量,代码实例如下:


1

2

3

scala> tree.root match {

         case TreeNode(_, leftNode@TreeNode("left",_,_), _) => leftNode

        }

上面代码中,leftNode变量 使用@符号绑定到匹配的左节点上,正如代码中写的,匹配成功后返回左节点

四、模式守卫(pattern guard)

模式守卫能够重新制定匹配规则, 代码实例如下:


1

2

3

4

5

6

7

abstract class Expr

    case class Number(num: Double) extends Expr

    case class BinOp(operator: String,left: Expr, right: Expr) extends Expr

def simplifyTop(expr: Expr): Expr = expr match {

  case BinOp("+",x,y) if x==y => BinOp("*",x,Number(2))

}

上面的代码中,对模式进行了简化,对表达式(x+x)?转换成(x*2)。

五、封闭类

当使用样本类做模式匹配时,必须确信考虑到所有可能的情况,有时候可以通过添加默认处理做到这点,这里还有一种方法,那就是封闭类。

将样本类的超类声明为sealed,则该类为封闭类,则除了封闭类所在的文件之外不能添加任何新的子类,即子类与密封类在一个文件中定义。定义为封闭类之后,编译器能够确保已经列出所有可能的选择。


1

2

3

4

5

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

六、Option类型

Option类型用来表示可能存在也可能不存在的值,用于取代null值得使用。

Option有两种形式:Some(x),x是实际值,None表示没有值


1

2

3

4

scores.get("Alice") match {

  case Some(score) => println(score)

  case None => println("No score")

}

七、偏函数(Partial function)

偏函数到底是什么呢? 概括来说,偏函数是继承特质 PartialFunction的一个一元函数,它只在部分输入上有定义, 并且允许使用者去检查其在一个给定的输入上是否有定义。为此,特质 PartialFunction 提供了一个 isDefinedAt 方法。 事实上,类型 PartialFunction[-A, +B] 扩展了类型 (A) => B (一元函数,也可以写成 Function1[A, B] )。 模式匹配型的匿名函数的类型就是 PartialFunction 。

偏函数有两种定义方法


1

2

3

4

5

6

7

def p1:PartialFunction[Int, Int] = {

      case x if x > 1 => 1

  }

  

def p2 = (x:Int) => x match {

      case x if x > 1 => 1

  }

时间: 2024-08-26 02:27:27

Spark样本类与模式匹配的相关文章

样本类和模式匹配

一:样本类就是使用case关键字声明的类,和普通类的用法都一致. package 样本类和模式匹配 /** * @Author:Alex_lei * @Description: */ object Case_class { /** * people就是样本类 * @param name 名字属性 * @param age 年龄属性 */ case class people(name:String,age:Int) def main(args: Array[String]): Unit = { v

scala学习笔记(四)样本类与模式匹配

访问修饰符 格式:private[x]或protected[x],x指某个所属包.类或单例对象,表示被修饰的类(或方法.单例对象),在X域中公开,在x域范围内都可以访问: private[包名]:在该包名作用域内,被修饰域都能被访问: private[类名]:在该类的作用域,被修饰域都能被访问: private[this]:仅能在包含了定义的同一对象中访问,用于保证同一类中不能被其它对象访问: 例子: package tests{     private[tests] class Test{  

Scala学习文档-样本类与模式匹配

样本类:添加了case的类便是样本类.这种修饰符可以让Scala编译器自动为这个类添加一些语法上的便捷设定. //样本类case class //层级包括一个抽象基类Expr和四个子类,每个代表一种表达式 //样本类自动添加与类名一致的工厂方法 abstract class Expr case class Var(name:String) extends Expr//括号内参数不用加val,默认为加val的字段 case class Number(num:Double) extends Expr

【Scala】模式匹配和样本类

模式匹配 要理解模式匹配(pattern-matching),先把这两个单词拆开,先理解什么是模式(pattern),这里所的模式是数据结构上的,这个模式用于描述一个结构的组成. 我们很容易联想到"正则表达"里的模式,不错,这个pattern和正则里的pattern相似,不过适用范围更广,可以针对各种类型的数据结构,不像正则表达只是针对字符串.比如正则表达式里 "^A.*" 这个pattern 表示以A开头.后续一个或多个字符组成的字符串:List("A&

scala模式匹配及样本类

样本类 1.带有case关键字的类被称为样本类: 例如: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(operatot: String,left:Expr,right:Expr) exte

Spark RDD类源码学习(未完)

每天进步一点点~开搞~ abstract class RDD[T: ClassTag]( //@transient 注解表示将字段标记为瞬态的 @transient private var _sc: SparkContext, // Seq是序列,元素有插入的先后顺序,可以有重复的元素. @transient private var deps: Seq[Dependency[_]] ) extends Serializable with Logging { if (classOf[RDD[_]]

Spark关键类

sparkEnv (D:\Spark\SourceCode\spark\core\src\main\scala\org\apache\spark\SparkEnv.scala) Holds all the runtime environment objects for a running Spark instance (either master or worker), including the serializer, Akka actor system, block manager, map

spark reduce类操作

reduce类函数分析: ---------------------------------------------------------------------------- 待补全 ---------------------------------------------------------------------------- 要注意: 1.reduce先在各分区中做操作,随后进行整合. 2.reduce返回值类型和参加计算类型一样. 3.reduce过程不一定有shuffle的操作

【转】Scala基础知识

原文地址.续 课程内容: 关于这节课 表达式 值 函数 类 继承 特质 类型 apply方法 单例对象 函数即对象 包 模式匹配 样本类 try-catch-finally 关于这节课 最初的几个星期将涵盖基本语法和概念,然后我们将通过更多的练习展开这些内容. 有一些例子是以解释器交互的形式给出的,另一些则是以源文件的形式给出的. 安装一个解释器,可以使探索问题空间变得更容易. 为什么选择 Scala? ·表达能力     ·函数是一等公民     ·闭包 ·简洁     ·类型推断     ·