Scala高级特性

1.课程目标

  • 深入理解高阶函数 闭包函数 柯里化函数
  • 深入理解隐式方法 隐式变量以及隐式参数
  • 综合案例 模拟Spark任务调度

2.Scala中函数

2.1高阶函数

  • 函数是一个对象,如果一个方法的参数包含函数对象,那么这个方法就叫做高阶函数或者高阶方法
    
    格式:
    	def 方法名称((输入数据)=>{输出数据}):返回值类型={
            方法体
        }
    
    
    /**
      * 函数是一个对象,如果一个方法的参数包含函数对象,那么这个方法就叫做高阶函数或者高阶方法
      *
      * 格式:
      * def 方法名称((输入数据)=>{输出数据}):返回值类型={
      * 方法体
      * }
      */
    class Number(val x: Int) {
      //高阶函数
      def compute(f: Int => Int): Int = {
        f(x)
      }
    
      def square(x: Int): Int = {
        x * x
      }
    }
    
    
    object Scala_01_gaojie {
      def main(args: Array[String]): Unit = {
        val number = new Number(10)
        //求平方
        val f: Int => Int = (x: Int) => x * x
    
        println(number.compute(f)) //方法的功能依赖于函数参数
    
        //求立方
        val f2: Int => Int = (x: Int) => x * x * x
        println(number compute f2)
      }
    }
    
    
  • /**
      * ShopCar 类
      * 数组 Arrybuffer[Double] 装商品
      * 方法 add 添加商品价格功能
      * 方法:计算总价 (参数:函数 功能实现打折)
      */
    class ShopCar {
      val goods = ArrayBuffer[Double]()
    
      def add(g: Double): Unit = {
        goods += g
      }
    
      //结算
      def account(f: Double => Double): Double = {
        //1.计算所有商品的总价
        var sum: Double = 0.0
        goods.foreach((x: Double) => sum = sum + x)
        //2.通过函数进行打折
        f(sum)
      }
    }
    
    
    object Scala_02_gaojie {
      def main(args: Array[String]): Unit = {
        val car = new ShopCar
        car add 10
        car add 50
        car add 88.88
    
        //打折 满100 减50 满80 减20
        val f: Double => Double = (sum: Double) => {
          if (sum >= 100) {
            sum - 50
          } else if (sum >= 80) {
            sum - 20
          } else {
            sum
          }
        }
       val result=car account f
        println(s"消费:$result")
    
      }
    }
    

2.2高阶函数使用规则

  • 以Array.foreach 为例 说明函数的使用规则

  • object Scala_03_gaojie {
      def main(args: Array[String]): Unit = {
        val arr = Array[Int](1, 4, 5, 8, 9)
        //第一种遍历方式
        val f = (x: Int) => println(x)
        arr.foreach(f)
        println("--------------")
        //第二种方式 使用匿名函数
        arr.foreach((x: Int) => println(x))
        println("--------------")
        //第三种方式 省略数据类型 匿名函数
        arr.foreach(x => println(x))
        println("-----------------")
        //第四中方式  _ 占位符
        arr.foreach(println(_))
        println("-----------------")
        //第五中方式 省略占位符 省略小括号
        arr.foreach(println)
        println("-----------------")
        //map操作省略形式
        arr.map(_*2).foreach(println)
        println("-----------------")
      //  arr.map(x=>x*2)
        arr.filter(_%2==0).foreach(println)
    
        //reduce 操作
    
        println(arr.reduce(_+_))  //arr.reduce((x:Int,y:Int)=>x+y)
      }
    }
    

2.3闭包函数

  • 一个函数如果使用了外部变量,则这个函数就叫做闭包函数
    val a=10
    val f:Int=> Int =(x:Int)=>a+x  //方法内部,主要用来减少函数参数传递的
    val f2:(Int,Int)=>Int =(x:Int,y:Int)=x+y
    闭包函数:减少函数参数列表,是的函数操作相对更为简单
    

2.4柯里化函数

  • 概念:将一个具有复杂(多个)参数列表的方法转换成具有(多个简单参数列表)的方法,这个过程就叫做柯里化过程 ,具有多个简单参数列表的方法 ,就叫做柯里化方法
    柯里化函数: 柯里化函数是函数的级联操作
    柯里化函数实际上将每一步操作都返回一个function,也就说柯里化是将函数作为了方法的返回值
    
    def add(x:Int,y:Int)=x+y //正常方法
    def add(x:Int)(y:Int)=x+y  //柯里化方法
    class Number2(val x: Int) {
      //高阶函数
      def compute(f: Int => Int): Int = {
        f(x)
      }
    }
    
    object Scala_05_currying {
      def add(x: Int, y: Int) = x + y //正常方法
      /**
        * 如果一个方法具备多个参数列表,则可以分步骤进行操作
        * 如果参数是准备不完整的,我们可就通过柯里化进行延迟操作
        *
        * @param x
        * @param y
        * @return
        */
      def add2(x: Int)(y: Int) = x + y //柯里化方法
    
      def add3(x: Int)(y: Int)(z: Int) = x + y + z
    
      def add4(x:Int) = (y:Int)=>x+y
    
      def main(args: Array[String]): Unit = {
        println(add(1, 2))
        println(add2(1)(2))
        //柯里化方法分步操作
        val f = add2(1) _ //返回值是函数 function1
        println(f.toString())
        val result = f(2)
        println(result)
        println("-------------------")
    
        val number = new Number2(2)
    
        println(number.compute(f))
    
        println("-------------------")
    
        //add3 分步骤操作
        val f3=add3(1) _ //function1
        println(f3.toString())
        val f4=f3(2) // function1
        println(f4)
        val result2=f4(3)  //6
        println(result2)
    
        //柯里化函数: 柯里化函数是函数的级联操作
        val f5=add4(1)(2)  //function1
      }
    }
    
    

2.5柯里化应用场景

  • 统计就业率
    公式:已工作人数/班级人数
    定义:
    	def workRate(worker:Int,sum:Int)=worker.toDouble/sum.toDouble
    	def workRate2(sum:Int)(worker:Int)=worker.toDouble/sum.toDouble
    
    object Scala_06_currying {
      def workRate(worker: Int, sum: Int) = worker.toDouble / sum.toDouble
    
      def workRate2(sum: Int)(worker: Int) = worker.toDouble / sum.toDouble
    
      def main(args: Array[String]): Unit = {
        //需求 多次统计 7, 10 ,15 ,30,90  班级人数 100
        // 就业人数:50  80  90 98 100
        println( workRate(50,100))
        println( workRate(80,100))
        println( workRate(90,100))
        println( workRate(98,100))
        println( workRate(100,100))
    
        println("---------------------------------")
        //使用柯里化方法 规避掉冗余数据
        val f=workRate2(100) _
        println(f(50))
        println(f(80))
        println(f(90))
        println(f(98))
        println(f(100))
      }
    }
    柯里化函数能够减少方法参数的传递
    

3. 隐式转换操作

  • 格式: 能够将源数据类型转换成目标数据类型
    	implicit def 名称(源数据类型) =目标类型对象
    注意:隐式转换方法只能存在于object中
    
    class Dog {
      def run(): Unit = {
        println("飞奔吧小狗")
      }
    }
    
    class Teacher {
      def readScala(): Unit = {
        println("教学scala")
      }
    }
    
    class Bird {
      def fly(): Unit = {
        println("飞上天")
      }
    }
    
    object Scala_07_implicit {
      //implict def 名称(源数据类型) =目标类型对象
      implicit def dogToTeacher(d: Dog) = new Teacher
    
      //让狗飞上天
      implicit def dogToBird(d: Dog) = new Bird
    
      def main(args: Array[String]): Unit = {
        val dog = new Dog
        dog.run()
        dog.readScala()
        dog.fly()
      }
    }
    
    
    

3.1 隐式转换的对象类型

  • 总结:
    	1.隐式转换方法可以将一个对象转换成函数对象
    	2.隐式转换方法可以将一个对象转换成单例对象Object
    	3.具有继承关系的子类可以继承父类的隐式转换功能
    import java.io.File
    
    import scala.io.Source
    
    class Person
    
    class Sun extends Person
    
    object Scala_08_implicit {
      //person能否通过隐式转换将函数
      implicit def personToFunction(p: Person) = (x: Int) => x * x
    
      //person对象能否转换成object对象呢 Source
      implicit def personToSource(p: Person) = Source
    
      def main(args: Array[String]): Unit = {
        val person = new Person
        println(person(10))
        println(person.fromFile(new File("D:/a.txt")).getLines().mkString(","))
        val sun = new Sun
        println(sun(100)) //10000
        println(sun.fromFile(new File("D:/a.txt")).getLines().mkString(","))
      }
    }
    

3.2隐式参数和隐式变量

  • 隐式变量:
    	implicit val/var 变量名称=值
    隐式参数 :修饰参数列表 implicit只能用在参数列表的最前边
    	def 方法名称(implicit 参数列表)
    总结:隐式变量可以给隐式参数提供默认参数。方法保留原始功能
    object Scala_09_implicit {
    
      //implicit val/var 变量名称=值
      implicit val a: Int = 100
      implicit val b: Long = 1000
      //implicit val c: Int = 80
      //def 方法名称(implicit 参数列表)
      def run(implicit speed: Int, time: Long): Unit = {
        println(s"以${speed}跑 时间${time}")
      }
    
      def main(args: Array[String]): Unit = {
        run
        run(90,9000)
      }
    }
    
    

3.3 隐式转换的管理

  • 一般情况,会将所有的隐式转换操作 都放在统一object中, 使用时需要进行导入
    import object._ //代表导入所有
    import object.名称 //名称可以是方法名称也可以是变量名称
    
    
    object CommonImplicitObject {
      //implict def 名称(源数据类型) =目标类型对象
      implicit def dogToTeacher(d: Dog) = new Teacher
    
      //让狗飞上天
      implicit def dogToBird(d: Dog) = new Bird
    
      //implicit val/var 变量名称=值
      implicit val a: Int = 100
      implicit val b: Long = 1000
      implicit val c: Int = 80
    }
    

3.4隐式转换的查找机制

  • 查找机制:
    	1.在当前隐式转换操作的文件或者object中进行查找
    	2.他在源数据类型的伴生对象中进行查找
    
    class Person10 {
      def say() = println("说话")
    }
    
    class Dog10 {
    
    }
    
    object Dog10 {
      implicit def dogToPerson(d: Dog10) = new Person10
    }
    
    object Scala_10_implicit {
    
      def main(args: Array[String]): Unit = {
        val dog1 = new Dog10
        dog1.say()
      }
    }
    
    

4.编程实战

  • 小目标:实现基于akka的spark底层通信机制

    • Spark

      • spark是基于内存的分布式大数据处理框架。主/多从
    • akka

      • akka是基于scala 开发的轻量级RPC通信框架 。高可用、高性能、可扩展。
    • akka编程模型Actor

      • 可以发送和接受消息
      • 多个actor可以并发执行
      • akka中actor能够创建和管理其他的actor
      • actor轻量级的编程模型,1GB=百万级的actor,支持高并发。
    • akka的两个核心编程类

      • ActorSystem
        • ActorSystem创建并且管理所有Actor,并且可以发送消息。在一个进程ActorSystem是单例存在的
      • Actor
        • preStart:在actor初始化(调用构造函数)完成之后直接调用的方法。做一些初始化工作逻辑,还有启动定时任务操作。该方法在整个actor声明周期只调用一次
        • receiver:接受消息并且根据消息进行相应的逻辑处理的。receiver可以循环接受消息

      想要基于akka的编程,首先创建的ActorSystem对象,要想基于akka的逻辑处理就必须创建actor。这里actor是有ActorSystem创建和管理的

  • 基于akka的两台终端进行互相通信

  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rglp5uR9-1592701249067)(C:\Users\tp\Desktop\23期scala\scala_day03\资料\两台机器互联图.png)]

  • master代码实现

    • package cn.itcast
      
      import akka.actor.{Actor, ActorSystem, Props}
      import com.typesafe.config.{Config, ConfigFactory}
      
      /**
        * 定义actor类
        */
      class Master extends Actor {
        override def receive: Receive = {
          case "test" => println("测试成功")
          case "slaver_regist" => {
            sender ! "SUC"
          }
        }
      }
      
      object Master {
        def main(args: Array[String]): Unit = {
          val host = "192.168.41.41"   //IP地址要修改成你们本机IP地址
          val port = 8888
          val configStr =
            s"""
               |akka.actor.provider="akka.remote.RemoteActorRefProvider"
               |akka.remote.netty.tcp.hostname="$host"
               |akka.remote.netty.tcp.port="$port"
            """.stripMargin
          val config = ConfigFactory.parseString(configStr)
          //创建actorSystem对象
          val masterActorSystem = ActorSystem.apply("masterActorSystem", config)
          //创建actor 自动启动actor
          val masterActor = masterActorSystem.actorOf(Props(new Master), "masterActor")
          masterActor ! "test"
      
          //akka.tcp://masterActorSystem@192.168.41.41:8888 在该链接之下,ActorSystem管理所有的acotr
          //也就是说 如果我们想要在其他进程中获取masterActor,则必须通过协议接口
      
        }
      }
      
  • slaver代码实现

    • package cn.itcast
      
      import akka.actor.{Actor, ActorSystem, Props}
      import com.typesafe.config.ConfigFactory
      
      class Slaver extends Actor {
        //创建完成后向master发送消息
        override def preStart(): Unit = {
          //需要获取masterAcotr对象
          val masterActor = context.actorSelection("akka.tcp://masterActorSystem@192.168.41.41:8888/user/masterActor")
          masterActor ! "slaver_regist"
        }
      
        override def receive: Receive = {
          case "slaver_test" => println("slaver_test 测试成功")
          case "SUC" => println("注册成功")
        }
      }
      
      object Slaver {
        def main(args: Array[String]): Unit = {
          val host = "192.168.41.41"  //IP地址要修改成你们本机IP地址
          val port = 9999
          val configStr =
            s"""
               |akka.actor.provider="akka.remote.RemoteActorRefProvider"
               |akka.remote.netty.tcp.hostname="$host"
               |akka.remote.netty.tcp.port="$port"
            """.stripMargin
          val config = ConfigFactory.parseString(configStr)
          //创建ActorSystem
          val slaverActorSystem = ActorSystem.apply("slaverActorSystem", config)
          //创建actor
          val slaverActor = slaverActorSystem.actorOf(Props(new Slaver), "slaverActor")
          slaverActor ! "slaver_test"
        }
      }
      

4.1 基于akka模拟实现spark的通信机制

  • 业务场景

    • master端:负责接收worker端的注册,并且管理worker。在管理过程总,能够自动检测worker状态,查看worker是否超时。如果worker超时则直接剔除掉,接受worker的心跳信息,并且更新worker状态
    • worker端:负责向master注册,并且提交其资源信息(cpu和内存),worker需要实时的向master发送心跳信息。
  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M57oU93Z-1592701249069)(C:\Users\tp\Desktop\23期scala\scala_day03\资料\Spark集群图.png)]

  • 分析业务模型:结合样例类和样例对象

    • 样例对象 CheckTimeOut
    • 样例类 Registed(msg:String) master反馈给worker信息
    • 样例类 WorkerRegist(workInto:WorkInfo) worker向master注册的信息
    • 类:WorkInfo(包含了worker资源信息(cpu和内存,workerId)) 资源对象
    • 样例对象 HeartBeat worker内部定时任务的标志
    • 样例类 HeartBeatMsg(workerId:String) 向master发送的心跳信息
  • 实现

    • master实现

    • package cn.itcast.master
      
      import akka.actor.{Actor, ActorSystem, Props}
      import cn.itcast.common._
      import com.typesafe.config.ConfigFactory
      
      import scala.collection.mutable
      import scala.concurrent.duration._
      
      class Master extends Actor {
        val interval_time = 3000
        //master用来管理woker信息的
        val workerMap = mutable.Map[String, WorkInfo]()
      
        val TIME_OUT = 5000 //超时时长
      
        override def preStart(): Unit = {
          //actor启动立即启动定时任务
          //定时任务定义
          import context.dispatcher
          //四个参数:
          // 1.延迟多长时间开始启动 定时任务
          //2. 间隔多长时间执行一次定时任务
          //3. Actor对象
          //4. 消息
          context.system.scheduler.schedule(0 millis, interval_time millis, self, CheckTimeOut)
        }
      
        override def receive: Receive = {
          case CheckTimeOut => {
            //检查worker的状态,worker是否超时
            for (t <- workerMap) {
              val workInfo = t._2
              if (System.currentTimeMillis() - workInfo.WORK_STATE > TIME_OUT) {
                workerMap -= t._1
              }
            }
            println("------------存活的worker信息---------------------")
            //打印存活的worker信息
            workerMap.foreach(t => println(t._2.toString))
          }
          //处理worker的注册
          case WorkerRegist(workInfo) => {
            //判断worker是否已经注册
            if (!workerMap.contains(workInfo.workerId)) {
              workInfo.WORK_STATE = System.currentTimeMillis() //直接更新状态
              workerMap += (workInfo.workerId -> workInfo) //注册成功了
              sender ! Registed("SUC")
            }
          }
          //接受worker端的心跳信息
          case HeartBeatMsg(workId) => {
            //更新worker的状态
            val workInfo: WorkInfo = workerMap.get(workId).get
            workInfo.WORK_STATE = System.currentTimeMillis()
          }
        }
      }
      
      
      object Master {
        def main(args: Array[String]): Unit = {
          val host = "192.168.41.41"
          val port = 8888
          val cfgStr =
            s"""
               |akka.actor.provider="akka.remote.RemoteActorRefProvider"
               |akka.remote.netty.tcp.hostname="$host"
               |akka.remote.netty.tcp.port="$port"
            """.stripMargin
          val config = ConfigFactory.parseString(cfgStr)
          //创建ActorSytem Actor对象
          val masterActorSystem = ActorSystem.apply("masterActorSystem", config)
          //创建Actor对象
          val masterActor = masterActorSystem.actorOf(Props(new Master), "masterActor")
        }
      }
      
    • worker端实现

      • package cn.itcast.worker
        
        import java.util.UUID
        
        import akka.actor.{Actor, ActorSelection, ActorSystem, Props}
        import cn.itcast.common._
        import cn.itcast.master.Master
        import com.typesafe.config.ConfigFactory
        
        import scala.concurrent.duration._
        
        class Worker extends Actor {
          //workerID
          val wid = UUID.randomUUID().toString
          val cpu = 24 + (math.random * 100).toInt
          val memory = 32 + (math.random * 100).toInt
          var masterActor: ActorSelection = _
        
          //向master进行注册
          override def preStart(): Unit = {
            val workInfo = new WorkInfo(wid, cpu, memory)
            //获取masteractor 发送消息
            masterActor = context.actorSelection("akka.tcp://masterActorSystem@192.168.41.41:8888/user/masterActor")
            masterActor ! WorkerRegist(workInfo)
          }
        
        
          override def receive: Receive = {
            //worker端接受到master的反馈信息
            case Registed(msg) => {
              println("注册成功")
              //启动定时任务
              import context.dispatcher
              context.system.scheduler.schedule(0 millis, 3000 millis, self, HeartBeat)
            }
            //接受心跳信息HeartBeat
            case HeartBeat => {
              //给master发送心跳信息
              masterActor ! HeartBeatMsg(wid)
            }
        
          }
        }
        
        object Worker {
          def main(args: Array[String]): Unit = {
            //构建ActorSystem Actor对象
            val host = "192.168.41.41"
            val port = 9999
            val cfgStr =
              s"""
                 |akka.actor.provider="akka.remote.RemoteActorRefProvider"
                 |akka.remote.netty.tcp.hostname="$host"
                 |akka.remote.netty.tcp.port="$port"
              """.stripMargin
            val config = ConfigFactory.parseString(cfgStr)
            //创建ActorSytem Actor对象
            val workerActorSystem = ActorSystem.apply("workerActorSystem", config)
            //创建Actor对象
            val workerActor = workerActorSystem.actorOf(Props(new Worker), "workerActor")
          }
        }
        
      • 对象

        • package cn.itcast.common
          
          /**
            * - 样例对象 CheckTimeOut
            * - 样例类 Registed(msg:String)  master反馈给worker信息
            * - 样例类 WorkerRegist(workInto:WorkInfo) worker向master注册的信息
            * - 类:WorkInfo(包含了worker资源信息(cpu和内存,workerId)) 资源对象
            * - 样例对象 HeartBeat    worker内部定时任务的标志
            * - 样例类 HeartBeatMsg(workerId:String)  向master发送的心跳信息
            */
          class BaseMsg extends  Serializable
          //样例对象 CheckTimeOut
          case object CheckTimeOut
          
          //样例类 Registed(msg:String)
          case class Registed(msg: String) extends  BaseMsg
          
          //类:WorkInfo
          class WorkInfo(val workerId: String, cpu: Int, memory: Int) extends  BaseMsg{
            var WORK_STATE: Long = 0 //保存worker状态信息
          
            override def toString: String = {
              "workerInfo:" + workerId + "\t" + cpu + "\t" + memory
            }
          }
          
          //样例类 WorkerRegist(workInto:WorkInfo)
          case class WorkerRegist(workInto: WorkInfo) extends BaseMsg
          
          //HeartBeat 样例对象
          case object HeartBeat
          
          //样例类 HeartBeatMsg(workerId:String)向master发送的心跳信息
          
          case class HeartBeatMsg(workId: String) extends BaseMsg
          

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值