黑猴子的家:Scala 控制抽象

1、控制抽象概念定义

控制抽象是一类函数
(1)参数是函数。
(2)函数参数没有输入值也没有返回值。
控制抽象,只关心过程,不关心结果,下面举了一个线程池的案例

2、没有参数,也没有返回值的函数

def play():Unit = {
}

3、使用示例

运行出来的效果,有点类似于线程池,我们先来说一下线程池这个概念是干嘛的,线程的创建和销毁是需要消耗大量时间的,如果说呢,我某一次业务当中,我有多次请求,这个请求是异步的,我每一个请求,我都建立一个线程去运行它,现在我有10个请求,同时执行,我需要建立10个线程去处理它,每个线程在执行完run方法后,它自动是不是要销毁,10个的话,就是10个创建,10个销毁,这个过程其实是挺消耗资源的,我们对这种业务场景进行优化,设计了一个线程管理者,维护下面线程的生命周期,把3个线程放进线程池里面,放进去后,让线程启动运行,它的run方法里面,肯定有东西,在run方法里面,写一个死循环,就不执行任何操作的死循环,用来确保我的run方法永远不会执行完,它run方法不执行完,这个线程就不会销毁,那么这个容器的作用是维护线程的生命周期,我们称这个容器为线程池,现在假设我还是有10个请求,我把这10个请求,放到缓冲队里里面,我这个线程池里面有三个线程,就表示我这个缓冲队列里面,一次可以取三个请求,当线程池里面,有一个线程执行完了后,就释放资源了,就表示有一个线程已经闲置了,但它不销毁,一旦结束任务,它进入一种空转的状态,等待下一个任务请求,用的还是以前实例化好的线程,没有重新创建,也没有销毁以前的线程,这是一种优化机制,因为线程的创建和销毁是极大的浪费资源的,我们现在这个Scala抽象控制,就类似于这个线程池的这个操作

没有参数,没有返回值,强调的是过程,线程做的这个事情,就是这样的,强调的是过程,多线程运行的时候,就是在加工数据和处理数据的过程,不会return一个东西出来,强调的是过程,我们如果真想把线程里面的结果带出来,怎么办呢,那不就是外部资源的访问嘛,
线程外部定义一个资源,因为我没法return,把线程加工好的数据return出去

def runInThread(f1:() => Unit): Unit ={
  new Thread{
    override def run(): Unit ={
      f1()
    }
  }.start
}

runInThread{
  () => println("干活咯!!")
    Thread.sleep(5000)
    println("干完活了")
}

这就是抽象控制

4、括号省略

//如果没有参数,小括号是可以省略的
//定义的时候没有加小括号,调用的时候也不要加小括号
//:Unit 也是可以省略的,但是我们没有必要删除,因为IDEA工具自动生成的,Scala是为了更好更快的开发而诞生的,不是为了增加操作复杂度的,编译器已经给你生成了,就不要再手动删除了,编译器没有自动生成,需要手写的时候,可以省略,唯一的目的是方便开发者,怎么方便,怎么来,就可以了

def runInThread1(f1: => Unit): Unit ={
  new Thread{
    override def run(): Unit ={
      f1
    }
  }.start
}

runInThread1{
    println("干活咯!!")
    Thread.sleep(5000)
    println("干完活了")
}

是不是很爽?是不是有点类似线程池的感觉,同一个线程,可以动态的向里面塞不同的任务去执行。

5、进阶用法

实现类似while的until函数
使用while循环打印 5 4 3 2 1 ,这是一个很基础的操作,我们使用控制抽象来实现一下
用递归的方式,实现了while循环,有的同学这时候,有疑问了,是不是所有的for循环和while循环在本质上,都是递归??答案是,不是的。我们后边会讲一下优化,就是伪递归优化,伪递归优化的意思就是,把递归函数的调用,给它隐式的转换成for循环,也就是我们能用for循环,就不要用递归,就是这个意思,因为递归太浪费内存栈了,因为每一个方法的调用,都意味着这个方法要压到栈里,它是独占一个栈针的,往上落好多层,是很消耗资源的,打印一个1到 10亿,如果用递归去做的话,栈溢出内存溢出,用for循环就没有事情,这就是我们尽量用for循环,而不用递归,什么时候用递归呢? 就是你不得不用递归的时候,才能用递归,逻辑比较复杂,用for循环封装的时候,不太方便,那你就用递归去操作,但你递归操作的时候,还可以优化,我们后边再说

这里呢,就是用递归实现了,while循环,掺杂了一个知识点,控制抽象

def until(condition: => Boolean)(block: => Unit) {
  if (!condition) {
    block
    until(condition)(block)
  }
}

var x = 10
//调用写法一
until(x == 0)(() => {x -= 1;println(x)})

//调用写法二
until(x == 0)({x -= 1;println(x)})

//调用写法三
until(x == 0){x -= 1;println(x)}

//调用写法四
until(x == 0){
  x -= 1;
  println(x)
}

尖叫提示:Scala 支持多范式编程

6、Scala控制抽象book

在Scala 中,我们可以将一系列语句归组成不带参数,也没有返回值的函数,举例来说,如下函数在线程中执行某段代码

def runInThread(block:() => Unit): Unit ={
  new Thread{
    override def run(): Unit = block()
  }.start()
}

这段代码以类型为()=> Unit 的函数的形式给出,不过,当你调用该函数时,需要写一段不那么美观的()=>

runInThread{() => println("hi");Thread.sleep(1000);println("Byte")}

要想在调用中省略()=> , 可以使用换名(call-by-name)调用表示法:在参数声明和调用该函数参数的地方省略去(),但保留=>

def runInThread(block: => Unit): Unit ={
  new Thread{
    override def run(): Unit = block
  }.start()
}

于是调用代码就变成了只是

runInThread{println("hi");Thread.sleep(1000);println("Byte")}

这看上去,就很棒。Scala程序员可以构建控制抽象(control abstraction):看上去像是编程语言的关键字的函数举例来说,我们可以实现一个用来完全像是在使用while语句那样的函数,或者,我们也可以再发挥一下,定义一个until语句,工作原理类似于while,只不过把条件反过来用

def until(condition: => Boolean)(block: => Unit) {
  if (!condition) {
    block
    until(condition)(block)
  }
}

以下是使用until的示例

var x = 10
until(x == 0){
  x -= 1;
  println(x)
}

这样的函数参数有一个专业术语,叫做换名调用参数。和一个常规(或者说换值调用)的参数不同,函数在被调用时,参数表达式不会被求值。毕竟,在调用until时,我们并不希望x == 0 被求值得到false。与之相反,表达式成为无参函数的函数体,而该函数被当作参数传递下去。

仔细看一下until函数的定义。注意它是柯里化的:函数首先处理掉condition,然后把block当作完全独立的另一个参数。如果没有柯里化,调用就会变成这个样子

until(x==0,{…})

用起来,就没有那么漂亮了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值