《快学Scala》(英文版:《Scala for the Impatient》),代码已传github:
https://github.com/vernonzheng/scala-for-the-Impatient
书为第一版。scala为2.11.4,jdk1.7.45,操作系统Mac OS X Yosemite 10.10.1。
第十八章 高级类型
18.1
实现一个Bug类,对沿着水平线爬行的虫子建模。move方法向当前方向移动,turn方法让虫子转身,show方法打印出当前的位置。让这些方法可以被串接调用。例如:
bugsy.move(4).show().move(6).show().turn().move(5).show()
上述代码应显示4 10 5。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| package _1801 { class Bug(var pos: Int = 0) { var forword: Int = 1 def move(up: Int):this.type = { pos += forword * up this } def show():this.type = { print(pos + " ") this } def turn():this.type = { forword = -forword this } } class Test extends App { val bugsy = new Bug bugsy.move(4).show().move(6).show().turn().move(5).show() } }
|
18.2
为前一个练习中的Bug类提供一个流利接口,达到能编写如下代码的效果:
bugsy move 4 and show and then move 6 and show turn around move 5 and show
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package _1802 { object then object show object around class Bug(var pos: Int = 0) { var forword: Int = 1 def move(num: Int): this.type = { pos += num; this } def and(obj: then.type): this.type = this def and(obj: show.type): this.type = { print(pos + " "); this} def turn(obj: around.type): this.type = { pos = 0; this} } class Test extends App { val bugsy = new Bug bugsy move 4 and show and then move 6 and show turn around move 5 and show } }
|
18.3
完成18.1节中的流利接口,以便我们可以做出如下调用:
book set Title to “Scala for the Impatient” set Author to “Cay Horstmann”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| class Document { var title: String = "" var author: String = "" def setTitle(title: String):this.type = {this.title = title; this} def setAuthor(author: String):this.type = {this.author= author; this} } object Title object Author class Book extends Document{ private var useNextArgAs:Any = null def set(obj: Any): this.type = {useNextArgAs = obj; this} def to(obj:String) = { useNextArgAs match{ case Title => setTitle(obj) case Author => setAuthor(obj) case _ => } this } } object Main extends App { val book = new Book book set Title to "Scala for the Impatient" set Author to "Cay Horstmann" println(book.title) println(book.author) }
|
18.4
实现18.2节中被嵌套在Network类中的Member类的equals方法。两个成员要想相等,必须属于同一个网络。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| import scala.collection.mutable.ArrayBuffer class Network { class Member(val name: String) { val contacts = new ArrayBuffer[Member] def canEqual(other: Any): Boolean = other.isInstanceOf[Member] override def equals(other: Any): Boolean = other match { case that: Member => (that canEqual this) && name == that.name case _ => false } } private val members = new ArrayBuffer[Member] def join(name: String) = { val m = new Member(name) members += m m } } val network1 = new Network().join("1") val network2 = new Network().join("1") println(network1.equals(network2))
|
18.5
考虑如下类型别名
type NetworkMember = n.Member forSome { val n : Network }
和函数
def process(m1: NetworkMember, m2: NetworkMember) = (m1, m2)
这与18.8节中的process函数有什么不同?
与18.8不同,允许不同网络作为参数
18.6
Scala类库中的Either类型可以被用于要么返回结果,要么返回某种失败信息的算法。编写一个带有两个参数的函数:一个已排序整数数组和一个整数值。要么返回该整数值在数组中的下标,要么返回最接近该值的元素的下标。使用一个中置类型作为返回类型。
1 2 3 4 5 6 7 8
| def getIndex(arr: Seq[Int], v: Int): Int Either Int = { if (arr.contains(v)) { Left(arr.indexOf(v)) } else { Right(arr.indexOf(arr.reduce((a,b) => if (math.abs(v - a) > math.abs(v - b)) b else a))) } } println(getIndex(Seq(2,3,4,5,-2,-3),1))
|
18.7
实现一个方法,接受任何具备如下方法的类的对象和一个处理该对象的函数。
调用该函数,并在完成或有任何异常发生时调用close方法。
def close(): Unit
1 2 3 4 5 6 7
| def tryWithClose[T<:{def close():Unit}](obj:T,func: T => Unit)={ try{ func(obj) }finally { obj.close() } }
|
18.8
编写一个函数printValues,带有三个参数f、from和to,打印出所有给定区间范围内的输入值经过f计算后的结果。这里的f应该是任何带有接受Int产出Int的apply方法的对象。例如:
printValues((x: Int) => x*x, 3, 6) //将打印 9 16 25 36
printValues(Array(1, 1, 2, 3, 5, 8, 13, 21, 34, 55), 3, 6) //将打印 3 5 8 13
1 2 3 4 5 6 7
| def printValues(f:{def apply(param:Int):Int}, from:Int, to:Int)={ for(i <- from to to) { print(f.apply(i) + " ") } } printValues((x: Int) => x*x, 3, 6) printValues(Array(1, 1, 2, 3, 5, 8, 13, 21, 34, 55), 3, 6)
|
18.9
考虑如下对物理度量建模的类:
abstract class DimT{
protected def create(v: Double): T
def + (other: Dim[T]) = create(value + other.value)
override def toString() = value + “ “ + name
}
以下是具体子类:
class Seconds(v: Double) extends DimSeconds{
override def create(v: Double) = new Seconds(v)
}
但现在不清楚状况的人可能会定义
class Meters(v: Double) extends DimSeconds{
override def create(v: Double) = new Seconds(v)
}
允许米(Meters)和秒(Seconds)相加。使用自身类型来防止发生这样的情况。
1 2 3 4 5 6 7 8 9 10 11 12 13
| abstract class Dim[T](val value: Double, val name: String){ this: T => protected def create(v: Double): T def + (other: Dim[T]) = create(value + other.value) override def toString() = value + " " + name } class Seconds(v: Double) extends Dim[Seconds](v, "s"){ override def create(v: Double) = new Seconds(v) } class Meters(v: Double) extends Dim[Seconds](v, "m"){ override def create(v: Double) = new Seconds(v) }
|
18.10
自身类型通常可以被扩展自身的特质替代,但某些情况下使用自身类型会改变初始化和重写的顺序。构造出这样的一个示例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| trait A { def sing() = "from a" } trait C { this: A => val w = sing + "from c" } class B{ this: C => val k = w } val b = new B with C with A println(b.k)
|
参考:
《快学Scala》:http://book.douban.com/subject/19971952/
(转载本站文章请注明作者和出处 Vernon Zheng(郑雪峰) – vernonzheng.com ,请勿用于任何商业用途)