scala04--Actor,高阶函数,闭包,柯里化,隐式转换,Option

一 Option类型

Option类型用来表示可能有值, 也可能没有值, 有两个子类

  1. None—>无值
  2. Some—>有值
class OptionDemo {
  def main(args: Array[String]): Unit = {
    //option中的参数相当于是一个元组, 
    val op: Option[(String, Int, Boolean)] = Some("小明", 18, true)
    //在Map中, 有两种取值方法-->get/getOrElse
    //若实际操作的是Option, name就使用getOrElse
    //使用元组接收数据
    val value: (String, Int, Boolean) = op.getOrElse(null)

    val map = Map(("xiaoli", 16), ("xiaozhang", 23), ("xiaosong", 43))
    val v: Option[Int] = map.get("xiaoli")
    v match {
      case Some(a) => println(a)
      case None => println("没有值")
    }
  }
}

偏函数
定义的方法实现体被包裹在一个大{} 内, 并且没有实现match关键字,而在实体中添加case class这样的方法叫做偏函数
def 方法名(参数列表) 返回值类型=>{
​ case 条件=>逻辑
​ //可以有很多case
}

//偏函数
object PianFuncDemo {
  //在不使用偏函数的前提下也可以完成(普通模式)
  def m1(num: String): Int = num match {
    case "one" => 1
    case "two" => 2
    case _ => -1
  }

  //这里还可以进一步简化-->偏函数
  //PartialFunction这是一个固定名字, 代表偏函数
  //第一个数据类型是参数, 即匹配条件的数据类型
  //第二个数据类型是返回值类型的数据类型即得到的结果
  def m2: PartialFunction[String, Int] = {
    case "one" => 1
    case "two" => 2
    case _ => -1
  }

  def main(args: Array[String]): Unit = {
    //调用偏函数方法
    println(m1("one")) //print 1
    println(m2("two")) //print 2
  }
}

二 获取线程池中的返回值Future

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class FutureDemo {
    // 线程池有4种创建的方式, 这里只演示一种
    public static void main(String[] args) {
        /**
         * 1. 创建固定大小的线程池
         * 创建固定大小线程池的时候, 建议genuine系统剩余线程数量类决定线程池的大小
         * int i = Runtime.getRuntime().availableProcessors();
         * ExecutorService threadPool = Executors.newCachedThreadPool();
         * 2. 创建一个可以缓存的线程, 如果线程长度超过了处理需求, 可以灵活的回收空闲线程
         * 如果没有后台线程会自动创建
         * 线程池是无限大的, 当执行第二个任务的时候, 若第一个任务已,经执行完成,会重复使用第一个执行任务的线程, 不会再重新创建
         */
        ExecutorService threadPool = Executors.newCachedThreadPool();
        //关闭线程池, threadPool.shutdown();
        //获取线程池中产生的数据
        //Future相当于是一个容器, 可以封装获取的数据
        Future<String> future = threadPool.submit(new Callable<String>() {
            //和线程创建中的run方法类似
            @Override
            public String call() throws Exception {
                System.out.println("Thread Name is " + Thread.currentThread().getName() + "\nThread ID is " + Thread.currentThread().getId());
                System.out.println("正在获取数据");
                System.out.println("正在读取数据");
                Thread.sleep(1000);
                System.out.println("读取完毕");
                return "success";
            }
        });

        //主线程需要睡眠, 否则会直接执行到最后, 导致get获取的时候没有值
        try {
            Thread.sleep(2000);
            if (future.isDone()) {
                //get方法如果获取到异常会被捕获
            } else {
                System.out.println("没有值");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


    }
}

三 Actor

并行: 同一时间点多个线程一起执行
并发: 同一时间段多个线程一起执行
Actor可以实现并行编程, 他是基于事件模型的并发机制, 运用Actor的消息收发机制实现多线程并行编程, Spark中使用AKKA的通信模型, 这里使用到Actor进行消息传送
看源码可知, Master和Worker之间的消息传递就是使用AKKA(AKKA实现的Actor)

3.1 特点:
  1. Actor数据不能共享
  2. 没有锁的概念,
  3. Actor之间通过信息进行通信
3.2 Actor发送消息的三种方式
  1. ! 发送异步消息, 没有返回值
  2. !? 发送同步消息, 有返回值, 需要进行线程等待
  3. !! 发送异步消息, 有返回值, 返回值使用Future[Any]接收
3.3 异步和同步
  1. 同步调用的时候会等待响应会返回值, 若没有响应会返回值, 就会阻塞线程, 不继续执行
  2. 异步调用就是单独的进行一个线程执行, 原始线程启动异步调用, 异步调用会调用另外一个线程执行请求, 原始线程不会等待这个异步线程, 彼此之间是用时执行, 只需异步执行完成后通知即可
3.4 案例
3.4.1 开启Actor

Actor执行顺序:

  1. 调用start 方法来启动Actor
  2. 执行Actor中act方法
  3. 向Actor发送信息(打印)
object ActorDemo1 {
  def main(args: Array[String]): Unit = {
      //启动Actor
    MyActor.start()
  }
}
import scala.actors.Actor

object MyActor extends Actor{
  //act 相当于Actor中的执行方法
  override def act(): Unit = {
    for (i <- 1 to 10){
      //打印值
      println("actor"+i)
      //做延迟, 防止开始即结束
      Thread.sleep(1000)
    }
  }
}
3.4.2 使用消息发送机制发送消息
import scala.actors.Actor

object MyActor2 extends Actor{
  override def act(): Unit = {
    //死循环
    while(true){
      //接收数据
      receive{
        //类似于偏函数的形式
        //因为需要传递信息, 信息中可能会包含着一些数据, 所以此时使用偏函数的形式是最合理的, 因为可以匹配消息类型
        //既然可以获取消息数据(即传递过来的信息), 所以使用样例类最合理
        case  AsyncMsg(id, msg) =>{
          //接收异步消息
          println("id: "+id+"   msg: "+msg)
          //接收到信息之后的回传信息
          //异步发送不需要返回值(已经接收到信息, 无需返回)
          sender ! ReplayMsg(0,"success")
        }
        case SyncMsg(id,msg)=>{
          //接收异步消息
          println("id: "+id+"   msg: "+msg)
          //接收到信息之后的回传信息
          //异步发送不需要返回值(已经接收到信息, 无需返回)
          sender ! ReplayMsg(1,"success")
        }
      }
    }
  }
}

//创建异步消息样例类
case class AsyncMsg(id:Int, msg:String)
//同步
case class SyncMsg(id:Int,msg:String)
//回传信息使用的样例类(异步或同步发送的时候会有返回值, 所以定义一个类进行消息发送)
case class ReplayMsg(id:Int,msg:String)
import scala.actors.Future

object ActorDemo2 {
  def main(args: Array[String]): Unit = {
    //启动Actor同时可以获取Actor对象
    //获取Actor目的就是为了发送消息
    val actor = MyActor2.start()
    //1. 发送异步消息无返回值
    actor ! AsyncMsg(1,"发送异步消息无返回值")
    //因为没有返回值, 所以只能自行打印
    println("异步发送消息, 没有返回值")

    //2. 发送同步消息, 有返回值, 会等待线程
    val value: Any = actor !? SyncMsg(2,"发送同步消息, 有返回值")
    println("同步消息发送完成")
    println(value)
    
    //3. 发送异步消息, 有返回值, 返回值是Any类型
    val values: Future[Any] = actor !! AsyncMsg(3,"异步消息,有返回值")
    //休眠一下
    Thread.sleep(1000)
    //判断是否有数据
    if(values.isSet){
      //取出数据
      val applyMsg: Any = values.apply()
      println(applyMsg)
    }else{
      println("Nothing")
    }
  }
}

四 高阶函数

  • 高阶函数其实就是作为方法中参数的传递(匿名函数, 闭包, 柯里化, 隐式转换函数), 函数作为方法的参数
  • [官方]高阶函数可以实现AOP(面向切片), 可以理解为面向对象的一种补充
4.1函数作为方法的参数

def 方法名(函数名(即方法的形参名): 函数中的形参的数据类型=>函数的返回值类型)

def method:(f:Int=>String,v:Int)={
//使用函数就相当于调用函数
f(v)
}
object HighFuncDemo {

  def method (f:Int=>String,v:Int)={
    f(v)
  }

  def main(args: Array[String]): Unit = {

    val str: String = method(x=>x.toString,5)
    println(str) //print 5
  }
}
4.2作为函数值的演示:函数的定义, 调用, 转换
object HighFuncDemo {
  def main(args: Array[String]): Unit = {
    //定义函数, 多条语句使用{  }
    val f1 = (x: Int) => x * 2
    //另外一种版本
    val f2: Int => Int = x => x * 2
    //多个参数
    val f3: (Int, Int) => Int = (x, y) => (x + y)

    //调用函数   函数名(参数) 如果函数没有返回值不建议接收
    val res1: Int = f1(1)

    //将当前数组每个元素*2
    val arr = Array(1, 2, 3, 4)
    val arr2: Array[Int] = arr.map(f2)

    //使用匿名函数
    arr.map((x: Int) => {
      x * 2
    })
    //简化
    arr.map(_ * 2)

    //转换: 将方法转换成函数
    //1. 显式转换
    def show() = {
      println("将要转换的方法")
    }

    val f4: () => Unit = show _
    f4()

    //2. 隐式转换  在调用系统提供API的时候m,参数可以是方法, 也可以是函数, 若是方法会自动转换
    def m(x: Int): Unit = {
      x * 2
    }

    arr.map(m)
  }
}

五 闭包

闭包是一个函数, 返回值依赖于声明在函数外部的一个或多个变量. 闭包通常来讲, 可以简单的认为是可以访问一个函数里边的局部变量另外一个函数

object ClosureDemo {
  def show(msg:String)=(name:String)=>println(msg+name)
  def main(args: Array[String]): Unit = {
    var i = 3
    val func=(j:Int)=>j*i
    println(func(3))

    //调用show方法
    //先将方法中的函数获取出来
    val showA = show("hello")
    //传入参数, 执行show方法
    showA("world")
  }
}

六 柯里化

柯里化就是把接收参数的函数转变成一个单一参数的函数
目前所接触过的函数中带有两个参数的有, fold(默认值)(计算逻辑)
柯里化的应用其实还有map, 其实map也是有两个参数的,但是第二个参数是一个隐式转换, 所以只传入一个参数即可
下面的代码只能进行模拟, 实际的应用时在源码中

object CurryingDemo {
  def main(args: Array[String]): Unit = {
    //定义一个方法 求两个数的和
    def curry(x: Int, y: Int) = x + y
    //调用
    println(curry(1, 2))

    //将当前的方法转换成柯里化的形式
    def curry2(x: Int)(y: Int) = x + y
    //调用
    println(curry2(1)(2))

    //上边的方法需要传入两个参数, 现在需要将参数进行减少, 但是还不影响计算, 对外只需传入一个参数,
    //implicit--->关键字, 可以进行隐式转换, 作为参数的时候无需调用, 可以自动执行
    def curry3(x:Int)(implicit y:Int=2)=x+y
    println(curry3(1))

    //现阶段柯里化的应用有fold和map
  }
}
  • 柯里化第二种方式–>闭包形式, 返回值是一个匿名的函数, 这个形式的应用在源码中有很多, 和正常的柯里化使用量是相同的
  • 函数的返回值是原有的第二个参数
object CurryingDemo2 {
  def curry(x:Int)=(y:Int)=>x+y
  def main(args: Array[String]): Unit = {
    val func: Int => Int = curry(2)
    println(func(2))
  }
}

七 隐式转换

隐式转换其实就是添加一个关键字[implicit], 这个关键字会触发隐式转换, 这种转换是自动执行的, 将一种类型转换为另一种类型

7.1 隐式转换的作用
object ImplicitDemo {
  def main(args: Array[String]): Unit = {
    //定义一个变量Int类型进行赋值, 赋值的数据是3.5会报错
    //创建隐式转换函数(方法)
    //这个隐式转换方法不需要手动调用, 自动执行, 当代码执行后会进行全局扫描
    //若当前作用域内有隐式转换函数会自动调用
    implicit def m(d: Double) = d.toInt
    val i: Int = 3.5
    println(i)
  }
}
7.2 使用引用转换对原有代码的增强
import java.io.File

import scala.io.Source



//隐式转换的作用
object ImplicitDemo {
    def main(args: Array[String]): Unit = {
      //提供隐式转换函数进行转换操作
      implicit def fileToRichFile(from:File)=new RichFile(from)
      val content: String = new File("Day04/Day04.iml").read
      println(content)
    }
}

//对系统File类进行扩展
class RichFile(val from:File){
  //读取方法, 这个方法可以直接读取文件里的内容
  //mkString相当于Java中的toString方法
  def read=Source.fromFile(from.getPath).mkString
}
7.3 跨类使用隐式转换
class Timo {
  val name="Time"
}
class Skill {
  def caimogu(timo: Timo,skill:String)=println(timo.name+"  "+skill)
}
//隐式转换所存在的类, 这个类最好是object, 这是一个不成文的规定
object Transform {
  implicit def learn(t:Timo)= new Skill
}
//执行隐式转换的操作
object ImplicitTest {
  def main(args: Array[String]): Unit = {
    //若需要使用隐式转换, 当前因为不在一个作用域内, 所以需要导包
    val timo = new Timo
    import Transform.learn
    timo.caimogu(timo,"test--"*5)
  }
}
7.4 隐式转换的时机
  1. 当前方法中参数的类型与目标类型不一致时, 发生隐式转换
  2. 当前对象调用所在类中不存在的方法或成员的时候, 编译会自动将当前对象进行隐式转换
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值