目录
7、模式匹配 match…case…
- Scala 提供了强大的模式匹配机制,应用也非常广泛。
- 一个模式匹配包含了一系列备选项,每个都开始于关键字 case 。
- 每个备选项都包含了一个模式及一到多个表达式。箭头符号 => 隔开了模式和表达式。
/**
* 注意点:
* 1.模式匹配不仅可以匹配值,还可以匹配类型
* 2.模式匹配中,如果匹配到对应的类型或值,就不再继续往下匹配
* 3.模式匹配中,都匹配不上时,会匹配到 case _ ,相当于default
*/
def matchTest(x: Any) = {
x match {
case 1 => println("result is 1")
case 2 => println("result is 2")
case 3 => println("result is 3")
case 4 => println("result is 4")
case x: Int => println("type is Int")
case x: String => println("type is String")
case x: Double => println("type is Double")
case _ => println("no match")
}
}
8、偏函数
8.1. 定义
-
偏函数(Partial Function),是一个数学概念它不是"函数"的一种, 它跟函数是平行的概念。
-
Scala中的Partia Function是一个Trait,其的类型为PartialFunction[A,B],其中接收一个类型为A 的参数,返回一个类型为B的结果。
-
例如:
{ case 1=>"hello world 1" case 2=>"hello world 2 } 模式匹配1,或者2,则会返回一个字符串 但是这里不能匹配除1,2之外的其他的Int,如果你传入一个3,那么不会有任何的返回 如果我们把这个当做一个函数来看待,那么他就是只接受Int类型且值为1或者2的函数调用 结论:偏函数相对于函数来讲,是缩小版的函数,或者说是残缺版的函数。
-
如果一个方法中没有 match 只有 case ,这个函数可以定义成 PartialFunction偏函数 。
-
偏函数定义时,不能使用括号传参,默认定义 PartialFunction 中传入一个值,匹配上了对应的 case ,返回一个值,只能匹配同种类型。
-
一个 case 语句就可以理解为是一段匿名函数。
def main(args: Array[String]): Unit = {
println(pf(1))
println(pf(6))
println(pf(true))
}
def pf: PartialFunction[AnyVal, String] = {
case 1 => "One"
case 2 => "Two"
case 3 => "Three"
case i: Int => "Int"
case i: Double => "Int"
case _ => "Other"
}
8.2. 方法
-
isDefinedAt : 这个函数的作用是判断传入来的参数是否在这个偏函数所处理的范围内。
-
orElse : 将多个偏函数组合起来使用,效果类似case语句。
-
andThen: 相当于方法的连续调用,比如g(f(x))。
-
applyOrElse:它接收2个参数,第一个是调用的参数,第二个是个回调函数。如果第一个调用的参 数匹配,返回匹配的值,否则调用回调函数。
-
isDefinedAt : 判断传入来的参数是否在这个偏函数范围内。
-
def main(args: Array[String]): Unit = { println(pf.isDefinedAt(1)) println(pf.isDefinedAt(4)) } def pf: PartialFunction[AnyVal, String] = { case 1 => "One" case 2 => "Two" case 3 => "Three" }
-
-
orElse : 将多个偏函数组合起来使用,效果类似case语句。
-
def main(args: Array[String]): Unit = { println(pf(1)) } def onePf: PartialFunction[Int, String] = { case 1 => "One" } def twoPf: PartialFunction[Int, String] = { case 2 => "Two" } def threePf: PartialFunction[Int, String] = { case 3 => "Three" } //组合在一起用 def pf = onePf orElse twoPf orElse threePf
-
-
andThen: 相当于方法的连续调用,比如g(f(x))。
-
def main(args: Array[String]): Unit = { var pf12 = onePf andThen twoPf println(pf12(1)) } def onePf: PartialFunction[Int, String] = { case 1 => "string" } def twoPf: PartialFunction[String, Double] = { case "string" => 2.0 }
-
-
applyOrElse:它接收2个参数,第一个是调用的参数,第二个是个回调函数。如果第一个调用的参 数匹配,返回匹配的值,否则调用回调函数。
-
def main(args: Array[String]): Unit = { println(onePf.applyOrElse(1, { num: Int => "more" })) println(onePf.applyOrElse(2, { num: Int => "more" })) } def onePf: PartialFunction[Int, String] = { case 1 => "one" }
-
9、样例类(case classes)
9.1. 概念
- case class是一种可以用来快速保存数据的类,可以认为是java中的pojo类,用于对象数据的保存。
- 默认实现方法:
- apply : 不需要使用new关键字就能创建该类对象
- unapply : 可以通过模式匹配来获取类属性,是Scala中抽取器的实现和模式匹配的关键方法。
- getter /setter : 默认构造参数默认被声明为val,实现了类构造参数的getter方法 如果构造参数是声明为var类型,实现setter和getter方法(不建议)
- toString : equals : hashCode : --》copy JavaBean规范的常见方法
9.2. 实现
object Hello08040018 {
def main(args: Array[String]): Unit = {
val user: SysUser = SysUser("admin", "123456", "管理员")
user match {
case SysUser(uname, passwd, nickname) => println(uname, passwd,
nickname)
}
}
}
//样式类
case class SysUser(uname: String, passwd: String, nickname: String)
10、隐式转换
10.1. 概念
- 隐式转换是在 Scala 编译器进行类型匹配时,如果找不到合适的类型,那么隐式转换会让编译器在 作用范围内自动推导出来合适的类型。
- 隐式的使用方式
- 1.将方法或变量标记为implicit
- 2.将方法的参数列表标记为implicit
- 3.将类标记为implicit Scala支持两种形式的隐式转换:
- 隐式值:用于给方法提供参数
- 隐式视图:用于类型间转换或使针对某类型的方法能调用成功
10.2. Scala隐式值
- 将p变量标记为implicit,所以编译器会在方法省略隐式参数的情况下去搜索作用域内的隐式值作为缺少参数。
- 如果此时你又在REPL中定义一个隐式变量,再次调用方法时就会报错 。不能定义两个隐式变量。
- 隐式转换必须满足无歧义规则,在声明隐式参数的类型是最好使用特别的或自定义的数据类型,不 要使用Int,String这些常用类型,避免碰巧匹配。
def main(args: Array[String]): Unit = {
person("admin")
implicit val name1 = "Harry"
// implicit val name2 = "Potter"
//省略隐式变量后自动搜索隐式值
person
}
//name为隐式参数
def person(implicit name: String) = println(name)
10.3. Scala隐式视图
隐式转换函数是指在同一个作用域下面,一个给定输入类型并自动转换为指定返回类型的函数,这个函数和函数名字无关,和入参名字无关,只和入参类型以及返回类型有关。注意是同一个作用域。
-
隐式转换为目标类型:把一种类型自动转换到另一种类型
def main(args: Array[String]): Unit = { hello("str") hello(123) hi("str") hi(123) } def hello(param: String): Unit = { println(param) } def hi(param: String): Unit = { println(param) } //隐式转换函数 implicit def typeConverter(param: Int): String = "yjxxt:" + param implicit def boolean2String(param: Boolean): String = "Boolean:" + param
-
隐式转换调用类中本不存在的方法
编译器在xiaoming对象调用时发现对象上并没有learn方法,此时编译器就会在作用域范围内 查找能使其编译通过的隐式视图 。
找到learnView方法后,编译器通过隐式转换将对象转换成具有这个方法的对象,之后调用 learn方法。
class Person { def learn(name: String) = println("正在学习【" + name + "】...") } //隐式转换函数 object Converter { implicit def learnView(b: Boy) = new Person implicit def learnView(g: Girl) = new Person } class Boy class Girl object HelloImplicitView { def main(args: Array[String]): Unit = { import com.yjxxt.p0805.Converter._ val xiaoming = new Boy //自动搜索隐式视图,调用其他方法 xiaoming.learn("数学") val xiaofang = new Girl xiaofang.learn("数学") } }
10.4. Scala隐式类
-
使用 implicit 关键字修饰的类就是隐式类。若一个 变量A 没有某些方法或者某些变量时,而这个 变量A 可以调用某些方法或者某些变量时
-
可以定义一个隐式类,隐式类中定义这些方法或者变量,隐式类中传入 A 即可。
-
注意点
- 隐式类必须定义在类,包对象,伴生对象中。
- 隐式类的构造必须只有一个参数,同一个类,包对象,伴生对象中不能出现同类型构造的隐式类。
class Rabbit(s:String){ val name = s } object Lesson_ImplicitClass { implicit class Animal(rabbit:Rabbit){ val tp = "Animal" def canFly() ={ println(rabbit.name +" can fly...") } } def main(args: Array[String]): Unit = { val rabbit = new Rabbit("rabbit") rabbit.canFly() println(rabbit.tp) } }
11、Actor Model
11.1. 概念
Actor Model 是用来编写并行计算或分布式系统的高层次抽象(类似 java 中的 Thread )让程序员不必为多线程模式下共享锁而烦恼。 Actors 将状态和行为封装在一个轻量的进程/线程中,但是不和其他 Actors 分享状态,每个 Actors 有自己的世界观,当需要和其他 Actors 交互时,通过发送事件和消息,发送是异步的,非堵塞的(fire-andforget),发送消息后不必等另外 Actors 回复,也不必暂停,每 个 Actors 有自己的消息队列,进来的消息按先来后到排列,这就有很好的并发策略和可伸缩性,可以建立性能很好的事件驱动系统。
2.12版本后,actor彻底从scala中抽离了出来,所以我们在使用前需要引入相应的lib。
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-actor_2.12</artifactId>
<version>2.5.9</version>
</dependency>
11.2. Actor的特征
- ActorModel 是消息传递模型,基本特征就是消息传递 .
- 消息发送是异步的,非阻塞的消息一旦发送成功,
- 不能修改 Actor 之间传递时,接收消息的
- actor 自己决定去检查消息, actor 不是一直等待,是异步非阻塞的。
11.3. 具体写法
11.3.1. Actor发送接收消息
import akka.actor.Actor
import akka.actor.ActorSystem
import akka.actor.Props
class HelloActor extends Actor {
override def receive: Receive = {
case "hey" => println("hey yourself")
case _ => println("hehe")
}
}
object Main extends App {
val system = ActorSystem("HelloSystem")
val helloActor = system.actorOf(Props[HelloActor], name = "helloActor")
helloActor ! "hey"
helloActor ! "good morning"
}
11.3.2. Actor与Actor之间通信
import akka.actor.{Actor, ActorRef, ActorSystem, Props}
class MyActor extends Actor {
override def receive: Receive = {
case msg: String => {
println(msg)
Thread.sleep(1000)
sender() ! "你说啥"
}
case Int => println("你竟然说数字")
case _ => println("default")
}
}
class MyActor2 extends Actor {
private val other: ActorRef = context.actorOf(Props(new MyActor),
"actor1child")
override def receive: Receive = {
case msg: String => {
println(msg)
other ! "nihao"
}
}
}
object Test extends App {
private val system: ActorSystem = ActorSystem("system")
private val actor: ActorRef = system.actorOf(Props(new MyActor2), "actor1")
actor ! "你好actor2"
}
12、Spark预热之WordCount
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.12</artifactId>
<version>2.4.6</version>
</dependency>
import org.apache.spark.SparkConf
import org.apache.spark.SparkContext
import org.apache.spark.rdd.RDD
import org.apache.spark.rdd.RDD.rddToPairRDDFunctions
object WordCount {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setMaster("local").setAppName("WC")
val sc = new SparkContext(conf)
val lines :RDD[String] = sc.textFile("./words.txt")
val word :RDD[String] = lines.flatMap{lines => {
lines.split(" ")
}}
val pairs : RDD[(String,Int)] = word.map{ x => (x,1) }
val result = pairs.reduceByKey{(a,b)=> {a+b}}
result.sortBy(_._2,false).foreach(println)
//简化写法
lines.flatMap { _.split(" ")}.map { (_,1)}.reduceByKey(_+_).foreach(println)
}
}