大白话 Scala 控制抽象
2019-04-14
关键字: Scala、Scala控制抽象、Scala高阶函数
本篇文章系笔者根据当前掌握的知识对 Scala 控制抽象的教材知识总结,不保证文章所述内容的绝对、完全正确性。
在 《快学Scala》 一书中,控制抽象被描述为是一系列语句的聚集,是一种特殊的函数,因为它是本质上只是对一系列语句的封装,所以它理应:
1. 没有参数输入;
2. 没有值返回。
教材中还给出了两段代码示例来解释控制抽象。但这两段代码对于 Scala 初学者来说,可能没那么好理解。这篇文章主要就针对教材中的示例作个白话解释。
第 1 个例子
def runInThread(block: () => Unit) {
new Thread {
override def run() {
block()
}
}
}
这个例子的功能倒好理解,它就是一个将传入的代码块放到一个全新的子线程里去执行的功能。这种场景在日常工作过程中是一很常见的场景,但是笔者认为这个例子对于初学者理解 控制抽象 却不是一个合适的例子。因为,有很多初学者甚至还没来得学习 Scala 中的对象实例化及匿名内部类的知识,虽然在 Scala 中它长的与 Java 非常像,但对于猜测的知识而言,心里始终会有个芥蒂。本来是只想学习控制抽象知识点的,突然穿插一个可能是匿名内部类,可能是线程的知识点进来,会加重读者学习负担,甚至打击学习信心。因此我准备换一个示例代码,这个示例代码除了控制抽象的知识以外,其余的全是单纯逻辑代码,不会对读者对控制抽象的学习造成额外的负担。
新的示例代码就定义一个类似 “购物” 的功能模块。封装一个 myShop 函数,这个函数开放给顾客调用,每次调用都表示一位顾客的购买行为。
def myShop(block: () => Unit) {
println("Welcome in!")
block()
println("Thanks for coming!")
}
好,接下来就开始我们的学习。
首先,上面我们有提到:控制抽象是一系列语句的聚集。因此,在示例代码中,千万不要觉得 def myShop 就是控制抽象。block 作为 “语句块” 才应该是控制抽象!

block 才是控制抽象
函数 myShop 的参数的变量名称是 block ,它的名称也已经很直白了,说明这里就应该传 “一块” 代码进来。而它的参数则是一个匿名函数类型 () => Unit ,它没有输入参数,也没有值返回。随后便是函数体的声明,这种函数名后面直接接大括号来写函数体的方式在 Scala 中被称为 “过程” ,简单说就是声明这个函数( myShop 函数 )是一个没有返回值的函数。在给这个 myShop 函数传参时,直接将你需要的语句块一骨脑扔进去就好。
《快学Scala》 中调用 runInThread 函数的方式是
runInThread {
() =>
println("Hi")
Thread.sleep(10000)
println("Bye")
}
类似地,在我们这个 myShop 函数中,也可以有如下调用方式
myShop {
() =>
println("I want a pencil")
println("I want a book")
println("I want your wechat")
}
myShop 例子的执行结果如下

emm... 这种调用方式乍看上去感觉像是在写一个函数体一样,感觉怪怪的。但其实只要你了解了 Scala 中 圆括号 与 大括号 的作用以后,这段代码你看起来就没什么毛病的了。
在 Scala 中,调用函数时,一般既可以使用圆括号来传参,也可以使用大括号来传参。不信?

scala 传参的方式
所以,上面不管是 runInThread 还是 myShop 的函数调用,都只是在传参而已。圆括号与大括号的区别在于可以传递的代码量而已。圆括号只能传递一条语句,而大括号可以传递多条语句。因此,对于我们的 myShop 的例子来说,假如某个人只想买一件商品,那么,完全可以使用以下的调用方式。同时,由于控制抽象函数不需要传参,所以一个 空的参数列表括号 在这也挺多余的,所以,函数声明和调用都可以简化一下。
def myShop(block: => Unit) {
println("Welcome in!")
block
println("Thanks for coming!")
}
def main(args: Array[String]){
myShop( println("I wanna buy a condom") )
}
怎么样,这样看起来是不是顺眼多了? 它的执行结果当然也是没毛病的。

当然,即使是只想买一件商品,使用大括号来调用也是没有问题的
myShop{ println("I wanna buy a pen") }
不管是 runInThread 函数还是我们的 myShop 函数,最重要的都是把传进来的代码块执行一次。有的同学可能会问:我为什么不直接单独封装我那一系列代码块,而非得复杂化成控制抽象来执行呢? 关于这点,我觉得如果您了解设计模式中的代理模式的话,可能就不会问出这个问题来了。附上一篇笔者多年前写的博文: 大白话设计模式之代理模式
第 2 个例子
《快学 Scala 》 中第 2 个例子是一个应用到柯里化与递归调用的控制抽象示例。
def until (condition: => Boolean) (block: => Unit) {
if(!condition) {
block
until(condition)(block)
}
}
在有了上面第 1 个例子的理解以后,对于这个例子的函数定义,应该就没什么问题的了。而关于这个函数的调用则是
var x = 10
until (x == 0) {
x -= 1
println(x)
}
相信这个函数调用,很多初学者都看的挺懵的,笔者也不例外。
这个例子书中说是模拟一个 while 表达式出来,看它的调用方式,也确实和 while 表达式一样。但是我们的 until 函数明明是定义了两个参数的,这里只传一个真的合适吗?
还是那句话:在理解了 Scala 中圆括号和大括号的作用以后,这段调用就显得各种没毛病了。这个调用中,确确实实是以柯里化的形式传了两个参数进去的。只不过第 1 个参数是以圆括号的形式来传递的,第 2 个参数由于是一系列的代码块,所以要用大括号的形式来传递。

花式柯里化调用
假如,我们在这个例子中的控制抽象函数只有一条语句的话,until 的调用形式完全可以改成下面这种更明朗的格式
var x = 10
until (x == 0)( x -= 1)
这是不是就很柯里化了?
The End
大白话 Scala 控制抽象的更多相关文章
- Scala控制抽象
private def filesHere = (new java.io.File(".")).listFiles() def filesEnding(query: String) ...
- scala编程(九)——控制抽象
减少代码重复 所有的函数都被分割成通用部分,它们在每次函数调用中都相同,以及非通用部分,在不同的函 数调用中可能会变化.通用部分是函数体,而非通用部分必须由参数提供.当你把函数值用做参数时,算法的非通 ...
- Scala 基础(十二):Scala 函数式编程(四)高级(二)参数(类型)推断、闭包(closure)、函数柯里化(curry)、控制抽象
1 参数(类型)推断 参数推断省去类型信息(在某些情况下[需要有应用场景],参数类型是可以推断出来的,如list=(1,2,3) list.map() map中函数参数类型是可以推断的),同时也可以 ...
- PL真有意思(六):子程序和控制抽象
前言 在之前我们把抽象定义为一种过程,程序员可以通过它将一个名字与一段可能很复杂的程序片段关联起来.抽象最大的意义就在于,我们可以从功能和用途的角度来考虑它,而不是实现. 在大多数程序设计语言中,子程 ...
- Programming In Scala笔记-第九章、控制抽象
本章主要讲解在Scala中如何使用函数值来自定义新的控制结构,并且介绍Curring和By-name参数的概念. 一.减少重复代码 1.重复代码的场景描述 前面定义的函数,将实现某功能的代码封装到一起 ...
- scala学习笔记:控制抽象
def repeat(n:Int)(action: =>Unit)=for(i<-1 to n)action var i = 0 repeat(5){ println(i=i+1) } 另 ...
- scala抽象类抽象字段
package com.test.scala.test /** * 抽象类学习,定义abstact关键字 */ abstract class AbstractClass { val id:Int;// ...
- scala控制流程语句
直接上代码了哈. package com.test.scala.test object Kongzi { def main(args: Array[String]): Unit = { //if 语句 ...
- 第2节 Scala中面向对象编程:9、getClass和classOf;10、调用父类的constructor;11、抽象类和抽象字段;
6.3.4. Scala中getClass 和 classOf Class A extends class B B b=new A b.getClass ==classOf[A] B b ...
随机推荐
- SqlSugar ORM 入门篇2 【查询】 让我们实现零SQL
SqlSugar在查询的功能是非常强大的,多表查询.分页查询 . 一对一查询.二级缓存.一对多查.WhenCase等复杂函数.Mapper功能.和拉姆达自定义扩展等,用好了是可以做到真正零SQL的一款 ...
- EF 外键不显示、如何让外键显示!增、删、改 操作时,外键不显示,只显示导航属性!
一.问题描述:EF 外键不显示.如何让外键显示!增.删.改 操作时,外键不显示,只显示导航属性! EF 添加.增加.插入数据时,外键不显示! 二.解决方案:在根据数据库生成模型的时候,选中“在模型中” ...
- vue学习记录③(路由)
上篇文章我们用vue-cli脚手架工具做了个简单的hello world页面,但是我们破坏了原来的流程,而正常的访问页面应该是通过路由来实现的. 那么什么是路由呢? 路由就是通过不同的url来访问不同 ...
- Your project path contains non-ASCII characters
Android studio导入project时报错 non-ASCII characters意味着中文字符报错,解决方法简单有效: 检查项目路径中是否出现中文名,将中文字符修改成英文就可以解决辽~
- vue环境搭建及项目介绍
搭建开发环境(搭建开发环境前必须安装node.js): 1.安装vue脚手架工具 $ npm install -g vue-cli 2.创建项目(注意项目名字不要有大写字母) vue init < ...
- Java:配置环境(Mac)——Eclipse;修改JDK版本后,Eclipse打不开
1.官网下载 选择一个速度快的镜像 推荐东软这个 2.双击下载的安装包,下一步 其中有一步是选择Eclipse版本,SE选第一个,EE第二个.仔细审题吧. 3.配置JDK 应用.关闭 4.测试:写个H ...
- 【English】九、kids/children/toddlers 三个单词的区别
一.child.kid.toddler 参考:https://www.zybang.com/question/a9150bb1239cf1d667135e9bd8518f75.html child:美 ...
- 教你一步永久激活WebStorm2018
工欲善其事必先利其器,我们在开发过程中,编辑器是我们提高开发效率及生产必备的工具,如何发现一个高效好用的编辑器是程序员必备的技能之一. 前端开发有众多编辑器 sublime.vscode.webstr ...
- ext当表单中的输入项为必填时,输入项label后显示红色的*
form表单里,当输入项为必填项时,需要将对应item的allowblank属性设置为true,如果item的label后面自带红色的*,表单中哪些输入项是“必填”,哪些输入项是“非必填”,一眼望去清 ...
- 从jsp到java文件再返回到前台页面的过程
客户端请求jsp页面总共分为三个阶段: <%@ page language="java" contentType="text/html; charset=utf-8 ...