Scala高级特性

1.课程目标

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

2.高阶函数 闭包函数 柯里化函数

2.1 高阶函数

  • 概念:如果一个方法的参数列表包含有函数对象,那么这个方法就称之为高阶函数或者高阶方法
  • 定义格式:
    • def 名称(FunctionN*):返回值类型={方法体,一定会使用到Function}
      
      /**
        * 类 Number
        * 主构造器: initNum Int类型
        * 高阶方法:compute  输入参数Int=>Int
        */
      class Number(val initNum: Int) {
        //高阶函数
        def compute(fun: Int => Int): Int = {
          fun(initNum)
        }
      }
      
      
      object Scala_day03_gaojie {
        def main(args: Array[String]): Unit = {
          val number = new Number(10)
          //使用高阶函数 求平方
          val fun = (x: Int) => x * x
          val result = number.compute(fun)
          println(result)
          //使用高阶函数 求立方
          val fun2 = (x: Int) => x * x * x
          val result2=number compute fun2
          println(result2)
      
        }
      }
      注意: 高阶函数操作 可以使得同一个方法具备不同的功能,功能依赖传入的参数
      
    • package cn.itcast.day03
      
      import scala.collection.mutable.ArrayBuffer
      
      /**
        * 类 ShopCar
        * 属性:集合 ArrayBuffer [Int]
        * 方法: add 添加
        * 高阶方法: account  参数:实现对总价数据进行打折
        */
      class ShopCar {
        val goods = new ArrayBuffer[Int]()
      
        def add(good: Int): Unit = {
          goods += good
        }
      
        def account(fun: Double => Double): Double = {
          val sum = goods.sum.toDouble //计算总价
          fun(sum)
        }
      }
      
      
      object Scala_day03_gaojie2 {
        def main(args: Array[String]): Unit = {
          val car = new ShopCar
          car add 100
          car add 120
          car add 150
          //定义打折策略 满150 减30 满120 10 满80 减5
          val fun = (sum: Double) => {
            if (sum >= 150) sum - 30
            else if (sum >= 120) sum - 10
            else if (sum >= 80) sum - 5
            else sum
          }
          val result = car.account(fun)
          println(result)
        }
      }
      
  • 高阶函数一些使用规则
    • object Scala_day03_gaojie3 {
        def main(args: Array[String]): Unit = {
          val arr = Array[Int](1, 2, 3, 4)
          //第一种使用方式
          val f = (x: Int) => println(x)
          arr.foreach(f)
          //第二种使用方式 匿名函数
          arr.foreach((x: Int) => println(x))
          //第三种使用方式 省略参数的数据类型
          arr.foreach(x => println(x))
          println("----------------")
          //第四种使用方式
          // 使用站位符的形式 :当函数传入的参数在函数体内 使用仅且使用一次的时候才能够使用站位符的形式
          arr.foreach(println(_))
          println("----------------")
          //第五种使用方式
          arr.foreach(println)
      
          //map函数 使用占位符的形式
          arr.map(_ * 2)
          //reduce函数
          arr.reduce((x, y) => x + y)
          arr.reduce(_ + _)
        }
      }
      

2.2 闭包函数

  • 闭包函数:
    	当一个函数的函数体内使用了外部变量的时候,这个函数就称之为闭包函数
    val a=10
    val fun=(x:Int)=>x+a  //闭包函数
    闭包函数作用:能够减少函数参数的个数,一般闭包函数 使用在方法内部
    

2.3 柯里化函数

  • 概念:将一个具有复杂参数列表的方法转换成具有多个简单参数列表的方法,这个转换过程就是柯里化过程,具有多个简单列表的方法 称之为柯里化方法或者柯里化函数
    
    注意: 柯里化函数实际上就是将函数作为了方法的返回值 并且能够实现函数的级联调用
    	  柯里化函数实现了 参数的延迟传递
          柯里化函数能够减少方法调用过程中参数的传递
    格式:
    	def 名称(参数列表)(参数类表)(参数列表):返回值类型={方法体}
    
    object Scala_day03_curring {
      def add(x: Int, y: Int) = x + y
    
      //柯里化函数
      def add2(x: Int)(y: Int) = x + y
    
      def add21(x: Int): Int => Int = {
        (y: Int) => x + y
      }
    
      def add3(x: Int)(y: Int)(z: Int) = x + y + z
    
      def main(args: Array[String]): Unit = {
        println(add(1, 2))
        println(add2(1)(2))
    
        //柯里化函数第二种调用方式 分步调用
    
        val a = add2(1) _
        println(a)
        val result = a(2)
        println(result)
    
        println("---------------")
        val f = add3(1) _ //function
        println(f)
        val f2 = f(2)
        println(f2)
        val result3 = f2(3)
        println(result3)
      }
    }
    
    
    object Scala_day03_curring3 {
      //定义求就业率
      def workRate(sum: Int, worker: Int) = worker.toDouble / sum.toDouble
    
      //柯里化函数
      //定义求就业率
      def workRate2(sum: Int)(worker: Int) = worker.toDouble / sum.toDouble
    
      def main(args: Array[String]): Unit = {
        val sum = 100
        //30天就业率
        println(workRate(sum, 10))
        //50天就业率
        println(workRate(sum, 20))
        //100 天就业率
        println(workRate(sum, 30))
        //180就业率
        println(workRate(sum, 100))
        println("-----------------")
        val workRateFun = workRate2(sum) _
        println(workRateFun(10))
        println(workRateFun(20))
        println(workRateFun(30))
        println(workRateFun(100))
      }
    }
    

3.隐式转换功能

  • 隐式转换功能能够实现 类与类之间的完全解耦

  • implicit def 方法名称(源数据类型)= new 目标数据类型
    
    注意:如果一个对象的方法,不存在与对象对应的类中以及不存在与继承关系中,则这个方法可能存在于隐式转换当中
    	 所有的隐式转换操作都必须存在于object对象中	
    
    class Dog
    
    class Teacher {
      def teach(): Unit = {
        println("教学")
      }
    }
    
    class Bird {
      def fly(): Unit = {
        println("飞起来")
      }
    }
    
    object Scala_day03_implicit {
      //implicit def 方法名称(源数据类型)= new 目标数据类型
      implicit def dogToTeacher(dog: Dog) = new Teacher
    
      implicit def dogToBird(dog: Dog) = new Bird
    
      def main(args: Array[String]): Unit = {
        val dog = new Dog
        dog.teach()
        dog.fly()
      }
    }
    
  • 隐式转换的数据类型

    • 1. 隐式转换可以将源数据类型 转换成一个单例对象
      2. 隐式转换可以将源数据类型转换成一个函数对象
      3. 具有继承关系的子类 能够继承父类的隐式转换操作
      
      object Scala_day03_implicit2 {
        //定义隐式转换
        implicit def personToSource(p: Person03) = Source
      
        implicit def personToFunction(p: Person03) = (x: Int) => {
          x + 1
        }
      
        implicit def javaObjectToScalaObject(list: util.LinkedList[String]) = {
          new ListBuffer[String]()
        }
      
      
        def main(args: Array[String]): Unit = {
          val person0 = new Person03
          person0.fromFile(new File("D:/a.txt")).getLines().toArray.foreach(println)
      
          println(person0(1))
          val son0 = new Son03
      
          println(son0(2))
          val linkList = new util.LinkedList[String]()
          linkList.add("sfsf")
          linkList.add("sfsdf")
        }
      }
      
  • 隐式转换可以修饰变量和方法的参数

    • 注意:隐式转换修饰方法参数列表
    • 1.隐式变量能够为隐式参数提供默认值。
      2.一般情况定义隐式变量的时候通常一种数据类型只定义一个变量
      
      object Scala_day03_implicit3 {
        //隐式变量
        implicit val a: Int = 100
        implicit val b: Long = 1000
      
        //方法 通过implicit关键词修饰方法的参数列表
      
        def run(implicit speed: Int, times: Long): Unit = {
          println("速度:" + speed + " 时长:" + times)
        }
      
        def main(args: Array[String]): Unit = {
          run
        }
      }
      
  • 隐式转换的管理

    • 隐式转换的管理:一般所有的隐式变量和隐式转换方法都放在统一的object文件中,然后通过import关键词进行导入
      	全部导入 import object文件名称._
      	精准导入 import object文件名称.{隐式转换变量名称,隐式转换方法名称}
      
  • 隐式转换的查找机制

    • 1.当前调用方法的object中去查找
      2.在源数据类型所对应的伴生对象中查找
      注意:所有的隐式转换操作都必须放在object中
      

4.案例基于Akka模拟Spark任务调度

  • Spark:是基于内存的分布式的计算引擎;主从 master slaver
  • AKKA: 是基于scala编写一个RPC通信框架,轻量级的通信框架,高性能、高可用、高扩展。
  • AKKA特点:
    • Akka的编程模型 Actor
    • akka可以进行并发执行
    • akka可以发送接受消息
    • akka中actor可以创建和管理其他的actor
    • akka中actor是一个轻量级的对象,1GB内存=百万级的actor对象。akka能够很好支持高并发应用
  • Akka中编程两个核心对象
    • ActorSystem
      • 主要用来创建Actor和管理Actor的,并且ActorSystem可以发送消息,ActorSystem在一个进程中是以单例形式存在的
    • Actor 两个核心方法
      • preStart :该方法在actor启动之前调用,并且在actor的生命周期过程中只调用一次。一般会将初始化操作或者定时任务放在该方法中。
      • receiver:可以循环接受消息,并且处理相应逻辑,还可以通过sender发送返回消息
    • 要想基于Akka的编程,首先要创建ActorSystem对象,要想进行业务逻辑处理,需要创建Actor对象,并且实现receiver抽象方法
  • 基于Akka的 两个进程之间的通信

  • master端代码
    • package cn.itcast
      
      import akka.actor.{Actor, ActorSystem, Props}
      import com.typesafe.config.ConfigFactory
      
      class Master extends Actor {
        override def receive: Receive = {
          case "test" => println("master 测试成功")
          case "regist" => {
            println("接收到slaver注册信息")
            //发送反馈消息
            sender!"registSuc"
          }
        }
      }
      
      
      object Master {
        def main(args: Array[String]): Unit = {
          val host = "192.168.23.27"
          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
          //创建config对象
          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.23.27:8888
          // 可以调用该接口获取 masterActorSystem 管理之下的任意一个actor
          //获取masterActor
          //akka.tcp://masterActorSystem@192.168.23.27:8888/user/masterActor
        }
      }
      
  • slaver端代码
    • package cn.itcast
      
      
      import akka.actor.{Actor, ActorSelection, ActorSystem, Props}
      import com.typesafe.config.ConfigFactory
      
      class Slaver(val masterHost: String, val masterPort: String) extends Actor {
        override def preStart(): Unit = {
          //启动时调用 向master发送消息
          //拿到masterActor
          val masterActor: ActorSelection = context.actorSelection(s"akka.tcp://masterActorSystem@$masterHost:$masterPort/user/masterActor")
          masterActor ! "regist"
        }
      
        override def receive: Receive = {
          case "test" => println("slave 测试成功")
      
          case "registSuc" => println("注册成功")
        }
      }
      
      object Slaver {
        def main(args: Array[String]): Unit = {
          val host = args(0)
          val port = args(1)
          val masterHost = args(2)
          val masterPort = args(3)
          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)
          val slaverActorSystem = ActorSystem.apply("slaverActorSystem", config)
          val slaverActor = slaverActorSystem.actorOf(Props(new Slaver(masterHost, masterPort)), "slaverActor")
          slaverActor ! "test"
        }
      }
      
  • 基于Akka的Spark底层通信

    • 结合样例类 构建业务模型(创建消息对象)
      • case class RegistMsg(WorkerInfo) 注册消息
      • class WorkerInfo(id,cup:Int,momery){ 状态属性} worker资源信息
      • case class RegistSuc(str:String) 注册成功消息
      • case object CheckTimeOut master定时任务检查slaver状态
      • case class HeartBeatMsg(workId) 接收和发送心跳的消息
      • case object HeartBeat slaver端定时发送心跳的定时任务
    • master端代码
      • package cn.spark.master
        
        import akka.actor.{Actor, ActorSystem, Props}
        import cn.spark.CommonMsg.{CheckTimeOut, HeartBeatMsg, RegistMsg, RegistSuc, WorkerInfo}
        import com.typesafe.config.ConfigFactory
        
        import scala.collection.mutable
        import scala.concurrent.duration._
        
        class SparkMaster extends Actor {
        
          val workerMap = mutable.Map[String, WorkerInfo]()
        
          val TIME_OUT = 10000
        
          override def preStart(): Unit = {
            //启动定时任务
            import context.dispatcher
            /**
              * initialDelay :master启动后延迟时间启动定时任务
              * interval: 定时任务执行的时间间隔
              * receiver: actor
              */
            context.system.scheduler.schedule(0 millis, 10000 millis, self, CheckTimeOut)
          }
        
          override def receive: Receive = {
            case RegistMsg(workerInfo) => {
              if (!workerMap.contains(workerInfo.workerid)) {
                //更新worker状态
                workerInfo.WORKER_STATER = System.currentTimeMillis()
                //将slaver信息管理在workerMap中
                workerMap += (workerInfo.workerid -> workerInfo)
        
                //发送反馈消息
                sender ! RegistSuc("suc")
              }
        
            }
            case CheckTimeOut => {
              //循环拿到slaver信息 判断slaver状态
              for (t <- workerMap) {
                val workerInfo = t._2
                if (System.currentTimeMillis() - workerInfo.WORKER_STATER > TIME_OUT) {
                  workerMap -= (t._1) //把已经挂掉的信息剔除掉
                }
              }
              println("------存活的slaver节点:" + workerMap.size + " 个")
              workerMap.foreach(t => println(t._2))
            }
            //master接受slaver的心跳信息
            case HeartBeatMsg(workId) => {
              if (workerMap.contains(workId)) {
                val workerInfo = workerMap.get(workId).get
                //更新workerInfo的状态
                workerInfo.WORKER_STATER = System.currentTimeMillis()
              }
            }
          }
        }
        object SparkMaster {
          def main(args: Array[String]): Unit = {
            val host = "192.168.23.27"
            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
            //创建config对象
            val config = ConfigFactory.parseString(configStr)
            //创建ActorSystem对象
            val masterActorSystem = ActorSystem.apply("masterActorSystem", config)
            //创建Actor 直接自动启动actor
            val masterActor = masterActorSystem.actorOf(Props(new SparkMaster), "masterActor")
            masterActor ! "test"
          }
        }
        
    • slaver端代码
      • package cn.spark.master
        
        import java.util.UUID
        
        import akka.actor.{Actor, ActorSelection, ActorSystem, Props}
        import cn.spark.CommonMsg.{HeartBeat, HeartBeatMsg, RegistMsg, RegistSuc, WorkerInfo}
        import com.typesafe.config.ConfigFactory
        
        import scala.concurrent.duration._
        
        class SparkSlaver(val masterHost: String, val masterPort: String) extends Actor {
          var masterActor: ActorSelection = _
           //构建workerId 
          val workerId = UUID.randomUUID().toString
          //自动分配cpu
          val cpu = (math.random * 200).toInt
          //自动生成memory
          val memory = (math.random * 200).toInt
        
          override def preStart(): Unit = {
            //向master发送消息
            masterActor = context.actorSelection(s"akka.tcp://masterActorSystem@$masterHost:$masterPort/user/masterActor")
            val workerInfo = new WorkerInfo(workerId, cpu, memory)
            masterActor ! RegistMsg(workerInfo)
          }
        
          override def receive: Receive = {
            case RegistSuc(msg) => {
              println("注册成功")
              //启动定时任务发送消息
              import context.dispatcher
              context.system.scheduler.schedule(0 millis, 5000 millis, self, HeartBeat)
            }
            case HeartBeat => {
              masterActor ! HeartBeatMsg(workerId)
            }
          }
        }
        
        object SparkSlaver {
          def main(args: Array[String]): Unit = {
            val host = args(0)
            val port = args(1)
            val masterHost = args(2)
            val masterPort = args(3)
            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)
            val slaverActorSystem = ActorSystem.apply("slaverActorSystem", config)
            val slaverActor = slaverActorSystem.actorOf(Props(new SparkSlaver(masterHost, masterPort)), "slaverActor")
            slaverActor ! "test"
          }
        }
        
posted on 2019-07-15 22:32  jeasonchen001  阅读(102)  评论(0)    收藏  举报