scala编程第19章学习笔记(1)——类型参数化
一、queues函数式队列
函数式队列是一种具有以下三种操作方式的数据结构:
head 返回队列的第一个元素。
tail 返回除第一个元素之外的队列。
scala> import scala.collection.immutable.Queue
import scala.collection.immutable.Queue scala> val q = Queue(1, 2, 3)
q: scala.collection.immutable.Queue[Int] = Queue(1, 2, 3)
实现函数式队列的一种简单方案是以 List作为功能的表达类型。这样,head和tail将转为List的相同操作,而append将变为连结操作。这将得到以下的代码实现:
scala> class SlowAppendQueue[T](elems: List[T]) {
| def head = elems.head
| def tail = new SlowAppendQueue(elems.tail)
| def append(x: T) = new SlowAppendQueue(elems ::: List(x))
| }
defined class SlowAppendQueue
上面实现的问题在于append操作。它花的时间与存储于队列的元素数量成正比。如果想要的是常量时间的append,可以尝试把表达List里面的元素倒序排列,这样原本最后添加进来的元素现在出现在List的最前面。如下实现:
scala> class SlowHeadQueue[T](smele: List[T]) {
| //smele是elems的反转
| def head = smele.last
| def tail = new SlowHeadQueue(smele.init)
| def append(x: T) = new SlowHeadQueue(x :: smele)
| }
defined class SlowHeadQueue
现在append是常量时间了,但head和tail不是。它们现在所花费的时间与存储在队列中的元素数量成正比。
另一种高效的实现方法:
scala> class Queue[T](
| private val leading: List[T], private val trailing: List[T]
| ) {
| private def mirror =
| if (leading.isEmpty)
| new Queue(trailing.reverse, Nil)
| else
| this
| def head = mirror.leading.head
| def tail = {
| val q = mirror
| new Queue(q.leading.tail, q.trailing)
| }
| def append(x: T) =
| new Queue(leading, x :: trailing)
| }
defined class Queue
二、信息隐藏
私有构造器及工厂方法
Java中,可以把构造器声明为私有的使其不可见。scala中,主构造器无须明确定义;不过虽然它的定义隐含于类参数及类方法体中,还是可以通过把private修饰符添加在类参数列表的前边把主构造器隐藏起来,如下所示:
scala> class Queue[T] private (
| private val leading: List[T],
| private val trailing: List[T]
| )
defined class Queue
夹在类名与参数之间的private修饰符表明Queue的构造器是私有的:它只能被类本身及伴生对象访问。类名Queue仍然是公开的,因此你可以继续使用这个类,但不能调用它的构造器:
scala> new Queue(List(1,2), List(3))
<console>:9: error: constructor Queue in class Queue cannot be accessed in object $iw
new Queue(List(1,2), List(3))
^
现在客户代码不能再使用Queue类的主构造器,需要有创建新队列的其他方法。一种可能方案是添加辅助构造器,如下:
def this() = this(Nil, Nil)
上面的辅助构造器可以构建空队列。通过改良,它可以带上初始队列元素列表:
def this(elems: T*) = this(elems.toList, Nil)
其中T*是重复参数的标记。
另一种可能性是添加可以初始元素序列创建队列的工厂方法。比较简洁的做法是定义与类同名的Queue对象及apply方法,如下所示:
object Queue {
//用初始元素’xs‘构造队列
def apply[T](xs: T*) = new Queue[T](xs.toList, Nil)
}
注意,工厂方法名为apply,因此客户可以用类似于Queue(1, 2, 3)这样的表达式创建队列。由于Queue是对象而不是函数,这个表达式会被扩展为Queue.apply(1,2,3)。
可选方案:私有类
私有构造器和私有成员是隐藏类的初始化代码和表达代码的一种方式。另一种更为彻底的方式是直接把类本身隐藏掉,仅提供能够暴露类公共接口的特质。
trait Queue[T] {
def head: T
def tail: Queue[T]
def append(x: T): Queue[T]
}
object Queue {
def apply[T](xs: T*):Queue[T] =
new QueueImpl[T](xs.toList, Nil)
private class QueueImpl[T](
private val leading: List[T],
private val trailing: List[T]
)extends Queue[T] {
def mirror =
if (leading.isEmpty)
new QueueImpl(trailing.reverse, Nil)
else
this
def head: T = mirror.leading.head
def tail: QueueImpl[T] = {
val q = mirror
new QueueImpl(q.leading.tail, q.trailing)
}
def append(x: T) =
new QueueImpl(leading, x :: trailing)
}
}
三、变化型注解
上面定义的Queue是特质,不是类型。因为它带有类型参数。结果,将不能创建类型为Queue的变量:
scala> def doesNotCompile(q: Queue) {}
<console>:8: error: trait Queue takes type parameters
def doesNotCompile(q: Queue) {}
^
取而代之,特质Queue能够指定参数化的类型,如Queue[String], Queue[Int],或Queue[AnyRef]:
scala> def doesCompile(q: Queue[AnyRef]) {}
doesCompile: (q: Queue[AnyRef])Unit
注:根据上面定义的Queue,不同元素类型的队列之间没有子类型关系。Queue[String]对象不能被用作Queue[AnyRef]。
然而,可以用如下的方式改变Queue类定义的第一行,以要求队列协变(弹性)的子类型化:
trait Queue[+T] {
def head: T
def tail: Queue[T]
def append(x: T): Queue[T]
}
在正常的类型参数前面加上+号表明这个参数的子类型化是协变(弹性)的。
除了+号之外,还可以前缀加上-号,这表明是需要逆变的(contravariant)子类型化。如果Queue定义如下:
trait Queue[-T] { ... }
那么如果T是类型S的子类型,这将隐含Queue[S]是Queue[T]的子类型。无论类型参数是协变的,逆变的,还是非协变的,都被称为参数的变化型。可以放在类型参数前的+号和-号被称为变化型注解。
scala编程第19章学习笔记(1)——类型参数化的更多相关文章
- scala编程第16章学习笔记(3)——List类的高阶方法
列表间映射:map.flatMap和foreach 1.xs map f 操作返回把函数f应用在xs的每个列表元素之后由此组成的新列表.如: scala> List(1, 2, 3) map ( ...
- scala编程第16章学习笔记(2)
转换列表:toIterator, toArray,copyToArray List类的toArray方法将递归存放的列表转换为连续存放的数组 Array类的toList方法将连续存放的数组转换为递归存 ...
- scala编程第16章学习笔记(1)
List列表的基本操作 head方法获得列表的第一个元素 tail方法获得列表除第一个元素之外的其它元素 isEmpty:判断列表是否为空,空的话返回真 last:获得列表最后一个元素 init:获得 ...
- scala编程第18章学习笔记——有状态的对象
银行账号的简化实现: scala> class BankAccount{ | private var bal: Int = 0 | def balance: Int = bal | def de ...
- scala编程第17章学习笔记(4)——元组
元组可以把固定数量的条目组合在一起以便于作为整体传送.不像数组或列表,元组可以保存不同类型的对象. 元组常用来返回方法的多个值.例如,下面的方法找到集合中的最长单词并返回它的索引: scala> ...
- scala编程第17章学习笔记(3)
可变(mutable)集合与不可变(immutable)集合 为了更易于完成不可变集合到可变集合的转换,或者反向转换,Scala提供了一些语法糖.纵使不可变集和映射并不支持真正的+=方法,Scala还 ...
- scala编程第17章学习笔记(2)——集和映射
默认情况下在使用“Set”或“Map”的时候,获得的都是不可变对象.如果需要的是可变版本,需要先写明引用. 如果同一个源文件中既要用到可变版本,也要用到不可变版本的集合或映射,方法之一是引用包含了可变 ...
- scala编程第17章学习笔记(1)——集合类型
列表 列表的初始化及对其首尾的访问: scala> val colors = List("red", "blue", "green") ...
- scala编程第16章学习笔记(4)——List对象的方法
通过元素创建列表:List.apply List(1, 2, 3) 等价于List.apply(1, 2, 3): scala> List.apply(1, 2, 3) res0: List[I ...
随机推荐
- js获取单个查询串的值
function getSearchString(key) { // 获取URL中?之后的字符 var searchArr= window.location.href.split('#')[0].sp ...
- 前端安全系列之二:如何防止CSRF攻击?
背景 随着互联网的高速发展,信息安全问题已经成为企业最为关注的焦点之一,而前端又是引发企业安全问题的高危据点.在移动互联网时代,前端人员除了传统的 XSS.CSRF 等安全问题之外,又时常遭遇网络劫持 ...
- JAVAEE——淘淘商城第一天:电商行业的背景和技术特点,商城的介绍、技术的选型、系统架构和工程搭建
1. 学习计划 1.电商行业的背景. 2.电商行业的技术特点 3.商城的介绍 a) 常用的名词介绍 b) 系统功能介绍 4.淘淘商城的系统架构 a) 传统架构 b) 分布式架构 c) 基于服务的架构 ...
- CSS3组件化之圆波扩散
本篇文章主要介绍用CSS3实现的水波扩散涟漪,圆波扩散,光圈扩散,雷达波向外散发动画. 预期效果应该是这样:,其实应该比这个更优美,因为设计师提供的gif出现透明度丢失问题,所以建议用css3实现. ...
- python 对字典"排序"
对字典进行排序?这其实是一个伪命题,搞清楚python字典的定义---字典本身默认以key的字符顺序输出显示---就像我们用的真实的字典一样,按照abcd字母的顺序排列,并且本质上各自没有先后关系,是 ...
- BZOJ.4552.[HEOI2016/TJOI2016]排序(线段树合并/二分 线段树)
题目链接 对于序列上每一段连续区间的数我们都可以动态开点建一棵值域线段树.初始时就是\(n\)棵. 对于每次操作,我们可以将\([l,r]\)的数分别从之前它所属的若干段区间中分离出来,合并. 对于升 ...
- python开发_tkinter_窗口控件_自己制作的Python IDEL_博主推荐(二)
在上一篇blog:python开发_tkinter_窗口控件_自己制作的Python IDEL_博主推荐 中介绍了python中的tkinter的一些东西,你可能对tkinter有一定的了解了.这篇b ...
- centos7 下出现 yum list 报错 还有yum groupolist 查询软件组列表报错
之前学到yum在线安装 不晓得那里出错了 跟着老师的教程走的 配置文件也看了 没有错误的 但还报错 这下面是报错的图 在这里说明一下带“#”的都是注释 可以不写的 这个 ...
- ServletActionContext.getRequest().getSession() 和 ActionContext.getContext().getSession()
ActionContext.getContext().getSession(); 这个方法获取的session是struts封装过的一个Map类型的session,只能调用put()方法缓存数据. S ...
- matlab运行过程中出现找不到指定模块问题解决
对于matlab08版本以前的解决方法: 修改改环境变量: 新建变量名:BLAS_VERSION 变量值:D:\matlab7\bin\win32\atlas_Athlon.dll 在你的安装文件夹里 ...