《快学scala》习题解答-第十九章-解析

《快学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。

第十九章 解析

19.1
为算术表达式求值器添加/和%操作符。

19.2
为算术表达式求值器添加^操作符。在数学运算当中,^应该比乘法的优先级更高,并且它应该是右结合的。也就是说,4^2^3应该得到4^(2^3),即65536。


参考:
《快学Scala》:http://book.douban.com/subject/19971952/

(转载本站文章请注明作者和出处 Vernon Zheng(郑雪峰) – vernonzheng.com ,请勿用于任何商业用途)

《快学scala》习题解答-第二十章-Actor

《快学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。

第二十章 Actor

20.1
编写一个程序,生成由n个随机数组成的数组(其中n是一个很大的值,比如1000000),然后通过将工作分发给多个actor的同时计算这些数的平均值,每个actor计算区间内的值之和,将结果发送给一个能组合出结果的actor。
如果你在双核或四核处理器上运行这个程序,和单线程的解决方案相比,会快多少?
双核上差不多

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import scala.actors.Actor
import scala.actors.Actor._
val taskCount = Runtime.getRuntime().availableProcessors()
case class SumPart(items:Array[Int],partCount:Int)
case class CollectSum(sum:Int,partCount:Int)
def randomArray(num:Int):Array[Int] = {
val result = new Array[Int](num)
for(i <- 0 to num-1) {
result(i) = (Math.random() * num).toInt
}
result
}
val ReduceActor = actor {
var result = 0D
var receiveCount = 0
loop{
receive{
case CollectSum(sum,partCount)=>
receiveCount += 1
result += sum
println(sum)
if(receiveCount>=partCount){
result = result/partCount
println("Actor result:"+result)
exit()
}
}
}
}
val MapActor = actor{
loop{
react{
case SumPart(items,partCount) =>
ReduceActor ! CollectSum(items.sum, partCount)
exit()
}
}
}
def actorMain(num:Int, partCount:Int) = {
val array = randomArray(num)
val startTime = System.currentTimeMillis()
var range = 1
if(array.length>=partCount)
range = array.length/partCount
var start = 0
var end = 0
var flag = false
MapActor.start()
while(!flag){
if(end+range>=array.length-1){
end = array.length - 1
flag = true
}else{
end = end+range
}
MapActor ! SumPart(array.slice(start,end),partCount)
start = start+range
}
println("Actor cost:"+(System.currentTimeMillis()-startTime))
}
def main(num:Int)={
val array = randomArray(num)
val startTime = System.currentTimeMillis()
println("No-Actor result:"+array.sum/array.length)
println("No-Actor cost:"+(System.currentTimeMillis()-startTime))
}
for(i<- 1 to 20) {
actorMain(100000, taskCount)
actorMain(100000, 100)
}
for(i<- 1 to 20) {
main(100000)
main(100000)
}

20.2
编写一个程序,读取一个大型图片到BufferedImage对象中,用javax.imageio.ImangeIo.read方法。使用多个actor,每一个actor对图形的某一个条带区域进行反色处理。当所有条带都被反色后,输出结果。

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import java.awt.image.BufferedImage
import java.io.File
import javax.imageio.ImageIO
import scala.actors.Actor
import scala.actors.Actor._
case class InversePart(bufImage:BufferedImage, imageRGB:Array[Int], start:Int, end:Int)
case class WriteImage(result:BufferedImage, imageRGB:Array[Int])
val inputPng = new File("./pic.png")
val outPng = new File("./pic_reverse.png")
class ImageActor(var count:Int = 0) extends Actor{
override def act()={
loop{
react{
case InversePart(bufImage, imageRGB, start, end) =>
for(i <- start to end){
imageRGB(i) = imageRGB(i)^0xffffffff
}
self ! WriteImage(bufImage, imageRGB)
case WriteImage(bufImage, imageRGB) =>
count -= 1
if(count==1) {
val width = bufImage.getWidth
val height = bufImage.getHeight
val resultImage = new BufferedImage(bufImage.getWidth, bufImage.getHeight, BufferedImage.TYPE_INT_RGB)
resultImage.setRGB(0, 0, width, height, imageRGB, 0, width)
ImageIO.write(resultImage , "png", outPng)
exit()
}
}
}
}
}
def inverse = {
val bufImage = ImageIO.read(inputPng)
if (bufImage != null) {
val width = bufImage.getWidth
val height = bufImage.getHeight
val imageRGB = new Array[Int](width * height)
bufImage.getRGB(0, 0, width, height, imageRGB, 0, width)
var step = 5
var start = 0
var end = 0
var flag = false
val count = imageRGB.length / step + 1
val imageActor = new ImageActor(count)
imageActor.start()
while (!flag) {
if (end + step >= imageRGB.length) {
end = imageRGB.length - 1
flag = true
} else {
end += step
}
imageActor ! InversePart(bufImage, imageRGB, start, end)
start += step
}
}
}
inverse

20.3
编写一个程序,对给定目录下所有子目录的所有文件中匹配某个给定的正则表达式的单词进行计数。对每一个文件各采用一个actor,另外再加上一个actor用来遍历所有子目录,还有一个actor将结果汇总到一起。

20.4
修改前一个练习的程序,显示所有匹配的单词。

20.5
修改前一个练习的程序,显示所有匹配的单词,每一个都带有一个包含它的文件的列表。

20.6
编写一个程序,构造100个actor,这些actor使用while(true)/receive循环,当接收到‘Hello消息时,调用println(Thread.currentThread),同时构造另外100个actor,他们做同样的事,不过采用loop/react。将它们全部启动,给它们全部都发送一个消息。第一种actor占用了多少线程,第二种actor占用了多少线程?

20.7
给练习3的程序添加一个监管actor,监控读取文件的actor并记录任何因IOException退出的actor。尝试通过移除那些计划要被处理的文件的方式触发IOException。

20.8
展示一个基于actor的程序是如何在发送同步消息时引发死锁的。

20.9
做出一个针对练习3的程序的有问题的实现,在这个实现当中,actor将更新一个共享的计数器。你能展现出程序运行是错误的吗?

20.10
重写练习1的程序,使用消息通道来进行通信。


参考:
《快学Scala》:http://book.douban.com/subject/19971952/

(转载本站文章请注明作者和出处 Vernon Zheng(郑雪峰) – vernonzheng.com ,请勿用于任何商业用途)

《快学scala》习题解答-第十八章-高级类型

《快学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 {
//非动词 non-verb
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) //将打印 9 16 25 36
printValues(Array(1, 1, 2, 3, 5, 8, 13, 21, 34, 55), 3, 6) //将打印 3 5 8 13

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)
}
//Meters can not extends Dim[Seconds] and compile fail

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 ,请勿用于任何商业用途)

《快学scala》习题解答-第十七章-类型参数

《快学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。

第十七章 类型参数

17.1
定义一个不可变类Pair[T,S], 带一个swap方法,返回组件交换过位置的新对偶

1
2
3
class Pair[T,S](val t:T,val s:S){
def swap() = new Pair(s,t)
}

17.2
定义一个可变类Pair[T],带一个swap方法,交换对偶中组件的位置。

1
2
3
class Pair[T](val s:T,val t:T){
def swap() = new Pair(t,s)
}

17.3
给定类Pair[T, S] ,编写一个泛型方法swap,接受对偶作为参数并返回组件交换过位置的新对偶。

1
2
3
class Pair[T,S](val t:T, val s:S){
def swap[T,S](t:T,s:S) = new Pair(s,t)
}

17.4
在17.3节中,如果我们想把Pair[Person]的第一个组件替换成Student,为什么不需要给replaceFirst方法定一个下界?

因为Student是Person的子类,是可以转成T类型的,不必定义下界

17.5
为什么RichInt实现的是Comparable[Int]而不是Comparable[RichInt]?

T <% Comparable[T] ,是隐含参数转化的,T是Int的时候将自动调用RichInt中的Comparable[RichInt]

17.6
编写一个泛型方法middle,返回任何Iterable[T]的中间元素。举例来说,middle(“World”)应得到’r’。

1
2
3
4
def middle[T](iter:Iterable[T]):T={
val seq = iter.toArray
seq(seq.length/2)
}

17.7
查看Iterable[+A]特质。哪些方法使用了类型参数A?为什么在这些方法中类型参数位于协变点?

如min, max, last 等。这些方法都会返回A类型,所以位于协变点。

17.8
在17.10节中,replaceFirst方法带有一个类型界定。为什么你不能对可变的Pair[T]定义一个等效的方法?
def replaceFirstR >: T { first = newFirst } //错误

因为first是子类,newFirst是超类,超类不能被赋值给子类。

17.9
在一个不可变类Pair[+T]中限制方法参数看上去可能有些奇怪。不过,先假定你可以在Pair[+T]定义
def replaceFirst(newFirst: T)
问题在于,该方法可能会被重写(以某种不可靠的方式)。构造出这样的一个示例。定义一个Pair[Double]的类型NastyDoublePair,重写replaceFirst方法,用newFirst的平方根来做新对偶。然后对实际类型为NastyDoublePair的Pair[Any]调用replaceFirst(“Hello”)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//Anyone resolve this, please contact me
package _1709 {
class Pair[+T](val t:T, val s:T) {
def replaceFirst[T](newFirst: T) {}
}
class NastyDoublePair[Double](t1:Double, s1:Double) extends Pair[Double](t1,s1) {
override def replaceFirst(newFirst: Double) ={
/**
* error: type mismatch;
found : Double(in class NastyDoublePair)
required: scala.Double
new NastyDoublePair(newFirst,math.sqrt(newFirst))
*/
new NastyDoublePair(newFirst,math.sqrt(newFirst))
}
}
object test extends App {
val p: Pair[Any] = new NastyDoublePair(1.0, 2.0)
p.replaceFirst("Hello")
}
}

17.10
给定可变类Pair[S,T],使用类型约束定义一个swap方法,当类型参数相同时可以被调用。

1
2
3
class Pair[S,T](val s:S, val t:T){
def swap(implicit env: S =:= T) = new Pair(t,s)
}

参考:
《快学Scala》:http://book.douban.com/subject/19971952/

(转载本站文章请注明作者和出处 Vernon Zheng(郑雪峰) – vernonzheng.com ,请勿用于任何商业用途)

开源分布式配置中心选型

一、目标

实现分布式配置中心:
(1)集中管理外部依赖的服务配置和服务内部配置
(2)提供web管理平台进行配置和查询
(3)支持服务注册与发现
(4)支持客户端拉取配置
(5)支持订阅与发布,配置变更主动通知到client,实时变更配置

备注:client为nodeJS,java等

二、开源解决方案调研

2.1 disconf

百度开源
与spring集成的很好,有web管理,client只支持java。

2.1.1 介绍

https://github.com/knightliao/disconf/wiki

2.1.2 源码

https://github.com/knightliao/disconf

2.2 diamond

阿里开源
阿里内部应用广泛,由http server(nameservers), diamond-server ,web组成,diamond-server连接同一个mysql,数据同步通过mysql dump文件同步(同步效率?),支持订阅发布,client只支持java。

2.2.1 介绍

http://code.taobao.org/p/diamond/wiki/index/

2.2.2 源码

http://code.taobao.org/svn/diamond/trunk

2.3 doozer

已停止更新,设计倾向于实时的数据变更通知,数据全部放于内存,不会持久化文件。(有独立工具支持,不知支持性怎样?)

2.4 etcd

CoreOS开源。
轻量级分布式key-value数据库,同时为集群环境的服务发现和注册而设计。
它提供了数据TTL失效(通过TTL更新来判断机器下线,来避免一定的网络分区问题)、数据改变监视、多值、目录监听、分布式锁原子操作等功能,来管理节点状态。

2.4.1 特性

  • 简单: curl可访问的用户的API(HTTP+JSON)
  • 安全: 可选的SSL客户端证书认证
  • 快速: 单实例每秒 1000 次写操作
  • 可靠: 使用Raft保证一致性
    注:zookeeper使用的比较复杂基于Paxos的Zab协议,etcd使用的Standford新的一致性算法Raft,概念上会简单些,0.4.6版本依赖于go-raft,0.5.0以上重新设计。

2.4.2 目前版本

网上的介绍文章基本停留到稳定版0.4.6上。目前版本已从0.x直接跳到了2.0版本。(2015-1-28)
https://coreos.com/blog/etcd-2.0-release-first-major-stable-release/
2.0介绍(需要翻墙):
https://www.youtube.com/watch?v=z6tjawXZ71E

使用go语言,部署简单,同时项目较年轻。

2.4.3 介绍

从实现原理到应用场景多方位解读(2015-1-30)
http://www.infoq.com/cn/articles/etcd-interpretation-application-scenario-implement-principle

2.4.4 源码

https://github.com/coreos/etcd

2.5 zookeeper

成熟的分布式配置解决方案,待续。。

2.6 比较etcd和zookeeper

Jason Wilder的一篇博客分别对常见的服务发现开源项目Zookeeper、Doozer、etcd进行了总结。
http://jasonwilder.com/blog/2014/02/04/service-discovery-in-the-cloud/

重点关注etcd是否能取代zookeeper。

2.6.1 性能

只从实现语言上考虑,golang性能近c,java在大型集群的多线程能力较好,总体相差不多。都能支持上千节点。

2.6.2 功能性:关注订阅发布、ttl特性

2.6.2.1 订阅发布

etcd 可以使用递归的Watcher,递归式的监控应用(主题)目录下所有信息的变动。这样就实现了机器IP(消息)变动的时候,能够实时通知到收集器调整任务分配。

递归订阅

1
2
3
etcd递归 watcher
$ etcdctl watch /foo-service --recursive
$ curl -L http://127.0.0.1:4001/v2/keys/foo-service?wait=true\&recursive=true

订阅通知,在/foo-service增加container2,返回:

1
2
3
4
5
etcd 递归监听 返回
$ etcdctl watch /foo-service --recursive
localhost:2222
$ curl -L http://127.0.0.1:4001/v2/keys/foo-service?wait=true\&recursive=true
{"action":"set","node":{"key":"/foo-service/container2","value":"localhost:2222","modifiedIndex":23,"createdIndex":23}}

2.6.2.2 TTL机制

更新方式

1
2
3
4
5
etcd ttl更新
$ etcdctl set /foo "Expiring Soon" --ttl 20
Expiring Soon
$ curl -L -X PUT http://127.0.0.1:4001/v2/keys/foo?ttl=20 -d value=bar
{"action":"set","node":{"key":"/foo","value":"bar","expiration":"2014-02-10T19:54:49.357382223Z","ttl":20,"modifiedIndex":31,"createdIndex":31}}

get ttl过期的数据,返回errorCode:100

1
2
3
4
5
etcd ttl 过期数据返回
$ etcdctl get /foo
Error: 100: Key not found (/foo) [32]
$ curl -L http://127.0.0.1:4001/v2/keys/foo
{"errorCode":100,"message":"Key not found","cause":"/foo","index":32}

2.6.3 部署结构

etcd支持普通节点模式和proxy模式两种:
启动设置集群大小,超过集群大小的etcd节点自动转化为proxy模式。

2.6.3.1 proxy模式

etcd作为一个反向代理把客户的请求转发给可用的etcd集群。这样,你就可以在每一台机器都部署一个Proxy模式的etcd作为本地服务,如果这些etcd Proxy都能正常运行,那么你的服务发现必然是稳定可靠的。
如图:

etcd-proxy

2.6.4 总结

etcd特性略胜于zookeeper两点:
(1)etcd在订阅发布机制上能提供的功能与zookeeper相似。但是更轻量级,使用api更简单,依赖少,可直接使用curl/http+json或etcdctl的方式。
(2)etcd的TTL机制能避免一定的网络分区问题(如网络间断误认为注册服务下线)

zookeeper胜于etcd两点:
(1)成熟,稳定性高,多数坑已被踩过。
(2)配套工具:etcd没有web监控平台,client有node-etcd 3.0,较年轻。zookeeper有简单易用的exhibitor监控,java client的curator替代zkclient,非常成熟易用,避免掉坑。

2.6.4.1 综合实际情况

线上已有zookeeper集群,考虑部署成本,避免踩坑,和zookeeper稳定和成熟配套工具,风险等,建议基于zookeeper进行封装开发。
如果小型集群,也是可以尝试etcd,毕竟架构部署简单省事。


参考:

(转载本站文章请注明作者和出处 Vernon Zheng(郑雪峰) – vernonzheng.com ,请勿用于任何商业用途)

《快学scala》习题解答-第十六章-XML处理

《快学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。

第十六章 XML处理

16.1

(0)得到什么?(0)(0)呢?为什么?

1
2
3
4
5
scala> println(<fred/>(0))
<fred/>
scala> println(<fred/>(0)(0))
<fred/>

因为都是scala.xml.Node,是NodeSeq的子类,等同于长度为1的序列。

16.2
如下代码的值是什么?


  • Opening bracket:[

  • Closing bracket:]

  • Opening bracket:{

  • Closing bracket:}


你如何修复它?

1
2
3
4
5
6
7
8
9
10
11
12
13
scala> <ul>
| <li>Opening bracket:[</li>
| <li>Closing bracket:]</li>
| <li>Opening bracket:\{\{</li>
| <li>Closing bracket:\}\}</li>
| </ul>
res9: scala.xml.Elem =
<ul>
<li>Opening bracket:[</li>
<li>Closing bracket:]</li>
<li>Opening bracket:{</li>
<li>Closing bracket:}</li>
</ul>

花括号作为字面量,需要连写两个

16.3
对比

  • Fred
  • match { case
  • {Text(t)}
  • => t }

  • {“Fred”}
  • match { case
  • {Text(t)}
  • => t}
    为什么它们的行为不同?

    scala 2.11.4查不到Text api。。略

    16.4
    读取一个XHTML文件并打印所有不带alt属性的img元素

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import scala.xml.XML
    var html = "<html><head><title>第一个网页</title></head><body><p><img alt='a'><img src='1'></img></p></body></html>"
    val images = (html \\ "img").filterNot(_.attributes("alt").isDefined)
    val images1 = html match{
    case n @ <img/> if (!n.attributes("alt")) => n
    }
    println(images.mkString("\n"));
    println(images1.mkString("\n"));

    16.5
    打印XHTML文件中所有图像的名称,即打印所有位于img元素内的src属性值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import scala.xml.XML
    var html = "<html><head><title>第一个网页</title></head><body><p><img alt='a'><img src='1'></img></p></body></html>"
    val images = (html \\ "img").flatMap(_.attributes("src"))
    val images1 = html match{
    case n @ <img/> => Some(n.attributes("src"))
    }
    println(images.mkString("\n"));
    println(images1.mkString("\n"));

    16.6
    读取XHTML文件并打印一个包含了文件中给出的所有超链接及其url的表格。即,打印所有a元素的child文本和href属性。

    1
    2
    3
    4
    5
    6
    import scala.xml._
    val html = ""
    val links = (html \\ "a") map { (x: Node) => (x.attribute("href").getOrElse("").toString, x.text) } filter {_._1.startsWith("http")}
    println(links.mkString("\n"));

    16.7
    编写一个函数,带一个类型为Map[String,String]的参数,返回一个dl元素,其中针对映射中每个键对应有一个dt,每个值对应有一个dd,例如:
    Map(“A”->”1”,”B”->”2”)
    应产出

    A
    1
    B
    2

    1
    2
    3
    4
    5
    6
    7
    def mapToHTML(map: Map[String, String]) = {
    <dl>{for ((k,v) <- map) yield <dt>{k}</dt><dd>{v}</dd>}</dl>
    }
    val x = Map("A" -> "1", "B" -> "2")
    println(mapToHTML(x))

    16.8
    编写一个函数,接受dl元素,将它转成Map[String,String]。该函数应该是前一个练习中的
    反向处理,前提是所有dt后代都是唯一的。

    1
    2
    3
    4
    5
    6
    7
    8
    def htmlToMap(str:String):Map[String,String] = {
    val dtMap = (str \\ "dt").toMap
    val ddMap = (str \\ "dd").toMap
    dtMap.zip(ddMap)
    }
    val html = <dl><dt>A</dt><dd>1</dd><dt>B</dt><dd>2</dd></dl>
    println(htmlToMap(html).mkString(","))

    16.9
    对一个XHTML文档进行变换,对所有不带alt属性的img元素添加一个alt=”TODO”属性,其他内容完全不变。

    1
    2
    3
    4
    5
    6
    7
    import scala.xml._
    val html = ""
    val replaceNoneAlt = html match{
    case img @ <img/> if(_.attribute(alt)=="NONE") => img % Attribute(null,"alt","TODO",null)
    case _ => html
    }
    println(replaceNoneAlt)

    16.10
    编写一个函数,读取XHTML文档,执行前一个练习中的变换,并保存结果。确保保留了DTD以及所有CDATA内容。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    import xml._
    import xml.transform._
    import xml.parsing.ConstructingParser
    import xml.dtd.DocType
    import java.io.File
    val doc = ConstructingParser.fromFile(new File("./test.xml"),
    true).document
    val a = doc.docElem
    val imgRule = new RewriteRule {
    override def transform(n:Node) = n match {
    case i @ <img>{_*}</img> if(i.attribute("alt") == None) =>
    i % Attribute(null, "alt", "TODO", Null)
    case _ => n
    }
    }
    val result = new RuleTransformer(imgRule).transform(a)
    XML.save("./test.xml", result(0), "UTF-8", false,
    DocType("html", doc.dtd.externalID, Nil))

    参考:
    《快学Scala》:http://book.douban.com/subject/19971952/

    (转载本站文章请注明作者和出处 Vernon Zheng(郑雪峰) – vernonzheng.com ,请勿用于任何商业用途)

    《快学scala》习题解答-第十五章-注解

    《快学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。

    第十五章 注解

    15.1
    编写四个JUnit测试用例,分别使用带或不带某个参数的@Test注解。用JUnit执行这些测试

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    import org.junit.Test
    class ScalaTest {
    @Test
    def test1(){
    print("test1")
    }
    @Test(timeout = 1L)
    def test2(){
    print("test2")
    }
    }

    15.2
    创建一个类的示例,展示注解可以出现的所有位置。用@deprecated作为你的示例注解。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    @deprecated
    class Test{
    @deprecated
    val t = _;
    @deprecated(message = "unuse")
    def hello(){
    println("hello")
    }
    }
    @deprecated
    object Test extends App{
    val t = new Test()
    t.hello()
    t.t
    }

    15.3
    Scala类库中的哪些注解用到了元注解@param,@field,@getter,@setter,@beanGetter或@beanSetter?

    15.4
    编写一个Scala方法sum,带有可变长度的整型参数,返回所有参数之和。从Java调用该方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    import scala.annotation.varargs
    class Test {
    @varargs def sum(nums: Int*): Int = {
    nums.sum
    }
    }
    /** Test2.java
    class Test2 {
    public static void main(String[] args){
    Test t = new Test();
    System.out.println(t.sum(1,2,3));
    }
    }
    **/

    15.5
    编写一个返回包含某文件所有行的字符串的方法。从Java调用该方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    import io.Source
    class Test5{
    def read()={
    Source.fromFile("test.txt").mkString
    }
    }
    /*** Test2.java
    public class Hello {
    public static void main(String[] args){
    Test5 t = new Test5();
    System.out.println(t.read());
    }
    }
    ***/

    15.6
    编写一个Scala对象,该对象带有一个易失(volatile)的Boolean字段。让某一个线程睡眠一段时间,之后将该字段设为true,打印消息,然后退出。而另一个线程不停的检查该字段是否为true。如果是,它将打印一个消息并退出。如果不是,则它将短暂睡眠,然后重试。如果变量不是易失的,会发生什么?

    没区别

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    import concurrent.ops.spawn
    object Test06{
    @volatile var value = false
    }
    spawn {
    Thread.sleep(100);
    Test06.value = true
    println("Thread1: setting value to TRUE!")
    }
    spawn {
    while(!Test06.value) Thread.sleep(20);
    println("Thread2: value is TRUE!")
    }

    15.7
    给出一个示例,展示如果方法可被重写,则尾递归优化为非法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import annotation.tailrec
    object Test extends App{
    @tailrec
    def sum2(xs : Seq[Int],partial : BigInt) : BigInt = {
    if (xs.isEmpty) partial else sum2(xs.tail,xs.head + partial)
    }
    println(sum2(1 to 1000000,0))
    }

    15.8
    将allDifferent方法添加到对象,编译并检查字节码。@specialized注解产生了哪些方法?

    1
    2
    3
    object Test{
    def allDifferent[@specialized T](x:T,y:T,z:T) = x != y && x!= z && y != z
    }

    javap Test$得到

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    public final class Test$ extends java.lang.Object{
    public static final Test$ MODULE$;
    public static {};
    public boolean allDifferent(java.lang.Object, java.lang.Object, java.lang.Ob
    ject);
    public boolean allDifferent$mZc$sp(boolean, boolean, boolean);
    public boolean allDifferent$mBc$sp(byte, byte, byte);
    public boolean allDifferent$mCc$sp(char, char, char);
    public boolean allDifferent$mDc$sp(double, double, double);
    public boolean allDifferent$mFc$sp(float, float, float);
    public boolean allDifferent$mIc$sp(int, int, int);
    public boolean allDifferent$mJc$sp(long, long, long);
    public boolean allDifferent$mSc$sp(short, short, short);
    public boolean allDifferent$mVc$sp(scala.runtime.BoxedUnit, scala.runtime.Bo
    xedUnit, scala.runtime.BoxedUnit);
    }

    15.9
    Range.foreach方法被注解为@specialized(Unit)。为什么?通过以下命令检查字节码:
    javap -classpath /path/to/scala/lib/scala-library.jar scala.collection.immutable.Range
    并考虑Function1上的@specialized注解。点击Scaladoc中的Function1.scala链接进行查看

    首先来看Function1的源码

    1
    2
    3
    4
    5
    6
    7
    ......
    trait Function1[@specialized(scala.Int, scala.Long, scala.Float, scala.Double/*, scala.AnyRef*/) -T1, @specialized(scala.Unit, scala.Boolean, scala.Int, scala.Float, scala.Long, scala.Double/*, scala.AnyRef*/) +R] extends AnyRef { self =>
    /** Apply the body of this function to the argument.
    * @return the result of function application.
    */
    def apply(v1: T1): R
    ......

    可以看到Function1参数可以是scala.Int,scala.Long,scala.Float,scala.Double,返回值可以是scala.Unit,scala.Boolean,scala.Int,scala.Float,scala.Long,scala.Double 再来看Range.foreach的源码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    ......
    @inline final override def foreach[@specialized(Unit) U](f: Int => U) {
    if (validateRangeBoundaries(f)) {
    var i = start
    val terminal = terminalElement
    val step = this.step
    while (i != terminal) {
    f(i)
    i += step
    }
    }
    }
    ......

    首先此方法是没有返回值的,也就是Unit。而Function1的返回值可以是scala.Unit,scala.Boolean,scala.Int,scala.Float,scala.Long,scala.Double 如果不限定@specialized(Unit),则Function1可能返回其他类型,但是此方法体根本就不返回,即使设置了也无法获得返回值

    15.10
    添加assert(n >= 0)到factorial方法。在启用断言的情况下编译并校验factorial(-1)会抛异常。在禁用断言的情况下编译。会发生什么?用javap检查该断言调用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    object Test {
    def factorial(n: Int): Int = {
    assert(n > 0)
    n
    }
    def main(args: Array[String]) {
    factorial(-1)
    }
    }

    编译报错

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    Exception in thread "main" java.lang.AssertionError: assertion failed
    at scala.Predef$.assert(Predef.scala:165)
    at Test$.factorial(Test.scala:6)
    at Test$.main(Test.scala:11)
    at Test.main(Test.scala)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)

    禁用assert
    -Xelide-below 2011
    反编译此类javap -c Test$ 得到

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    ......
    public int factorial(int);
    Code:
    0: getstatic #19; //Field scala/Predef$.MODULE$:Lscala/Predef$;
    3: iload_1
    4: iconst_0
    5: if_icmple 12
    8: iconst_1
    9: goto 13
    12: iconst_0
    13: invokevirtual #23; //Method scala/Predef$.assert:(Z)V
    16: iload_1
    17: ireturn
    ......

    参考:
    《快学Scala》:http://book.douban.com/subject/19971952/

    (转载本站文章请注明作者和出处 Vernon Zheng(郑雪峰) – vernonzheng.com ,请勿用于任何商业用途)

    《快学scala》习题解答-第十四章-模式匹配和样例集合

    《快学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。

    第十四章 模式匹配和样例类

    14.1
    JDK发行包有一个src.zip文件包含了JDK的大多数源代码。解压并搜索样例标签(用正则表达式case [^:]+:)。然后查找以//开头并包含[Ff]alls?thr的注释,捕获类似// Falls through或// just fall thru这样的注释。假定JDK的程序员们遵守Java编码习惯,在该写注释的地方写下了这些注释,有多少百分比的样例是会掉入到下一个分支的?

    14.2
    利用模式匹配,编写一个swap函数,接受一个整数的对偶,返回对偶的两个组成部件互换位置的新对偶

    1
    2
    3
    4
    5
    6
    def swap[S,T](tup: (S,T))={
    tup match{
    case (a,b) => (b,a)
    }
    }
    println(swap[String,Int](("1",2)))

    14.3
    利用模式匹配,编写一个swap函数,交换数组中的前两个元素的位置,前提条件是数组长度至少为2

    1
    2
    3
    4
    5
    6
    7
    def swap(array:Array[Any])={
    array match{
    case Array(first,second,rest @_*)=> Array(second,first)++rest
    case _ => array
    }
    }
    println(swap(Array("1","2","3","4")).mkString)

    14.4
    添加一个样例类Multiple,作为Item的子类。举例来说,Multiple(10,Article(“Blackwell Toster”,29.95))描述的是10个烤面包机。当然了,你应该可以在第二个参数的位置接受任何Item,无论是Bundle还是另一个Multiple。扩展price函数以应对新的样例。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    abstract class Item
    case class Multiple(num : Int,item : Item) extends Item
    case class Article(description : String , price : Double) extends Item
    case class Bundle(description : String , discount : Double , item : Item*) extends Item
    def price(it : Item) : Double = it match {
    case Article(_,p) => p
    case Bundle(_,disc,its @ _*) => its.map(price _).sum - disc
    case Multiple(n,it) => n * price(it)
    }
    val p = price(Multiple(10,Article("Blackwell Toster",29.95)))
    println(p)

    14.5
    我们可以用列表制作只在叶子节点存放值的树。举例来说,列表((3 8) 2 (5))描述的是如下这样一棵树:
    *
    / | \

    • 2 *
      / \ |
      3 8 5
      不过,有些列表元素是数字,而另一些是列表。在Scala中,你不能拥有异构的列表,因此你必须使用List[Any]。编写一个leafSum函数,计算所有叶子节点中的元素之和,用模式匹配来区分数字和列表。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    def leafSum(list:List[Any]):Int={
    var total = 0
    list.foreach {
    lst =>
    lst match {
    case l: List[Any] => total += leafSum(l)
    case i: Int => total += i
    }
    }
    total
    }
    val l: List[Any] = List(List(3, 8), 2, List(5))
    println(leafSum(l))

    14.6
    制作这样的树更好的做法是使用样例类。我们不妨从二叉树开始。
    sealed abstract class BinaryTree
    case class Leaf(value : Int) extends BinaryTree
    case class Node(left : BinaryTree,right : BinaryTree) extends BinaryTree
    编写一个函数计算所有叶子节点中的元素之和。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    sealed abstract class BinaryTree
    case class Leaf(value : Int) extends BinaryTree
    case class Node(left: BinaryTree, right:BinaryTree) extends BinaryTree
    def leafSum(tree:BinaryTree):Int={
    tree match {
    case Node(a,b) => leafSum(a) + leafSum(b)
    case Leaf(v) => v
    }
    }
    val r = Node(Leaf(3),Node(Leaf(3),Leaf(9)))
    println(leafSum(r))

    13.7
    扩展前一个练习中的树,使得每个节点可以有任意多的后代,并重新实现leafSum函数。第五题中的树应该能够通过下述代码表示:
    Node(Node(Leaf(3),Leaf(8)),Leaf(2),Node(Leaf(5)))

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    sealed abstract class BinaryTree
    case class Leaf(value: Int) extends BinaryTree
    case class Node(tr: BinaryTree*) extends BinaryTree
    def leafSum(tree:BinaryTree):Int={
    tree match {
    case Node(r @_*) => r.map(leafSum).sum
    case Leaf(v) => v
    }
    }
    val r = Node(Node(Leaf(3), Leaf(8)), Leaf(2), Node(Leaf(5)))
    println(leafSum(r))

    13.8
    扩展前一个练习中的树,使得每个非叶子节点除了后代之外,能够存放一个操作符。然后编写一个eval函数来计算它的值。举例来说:
    +
    / | \

    • 2 -
      / \ |
      3 8 5
      上面这棵树的值为(3 * 8) + 2 + (-5) = 21
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    sealed abstract class BinaryTree
    case class Leaf(value: Int) extends BinaryTree
    case class Node(op: Char, leafs: BinaryTree*) extends BinaryTree
    def eval(tree:BinaryTree):Int= {
    tree match {
    case Node(op, leafs@_*) => op match {
    case '+' => leafs.map(eval _).sum
    case '-' => -leafs.map(eval _).sum
    case '*' => leafs.map(eval _).product
    }
    case Leaf(x) => x
    }
    }
    val x = Node('+', Node('*', Leaf(3), Leaf(8)), Leaf(2), Node('-', Leaf(5)))
    println(x)
    println(eval(x))

    14.9
    编写一个函数,计算List[Option[Int]]中所有非None值之和。不得使用match语句。

    1
    2
    3
    4
    5
    def sum(lst: List[Option[Int]]) = lst.map(_.getOrElse(0)).sum
    val x = List(Some(1), None, Some(2), None, Some(3))
    println(sum(x))

    14.10
    编写一个函数,将两个类型为Double=>Option[Double]的函数组合在一起,产生另一个同样类型的函数。如果其中一个函数返回None,则组合函数也应返回None。例如:
    def f(x : Double) = if ( x >= 0) Some(sqrt(x)) else None
    def g(x : Double) = if ( x != 1) Some( 1 / ( x - 1)) else None
    val h = compose(f,g)
    h(2)将得到Some(1),而h(1)和h(0)将得到None

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    def compose(f:Double=>Option[Double],g:Double=>Option[Double])={
    (x : Double) =>
    if (f(x) == None || g(x) == None) None
    else g(x)
    }
    import scala.math.sqrt
    def f(x : Double) = if ( x >= 0) Some(sqrt(x)) else None
    def g(x : Double) = if ( x != 1) Some( 1 / ( x - 1)) else None
    val h = compose(f,g)
    println(h(2))

    参考:
    《快学Scala》:http://book.douban.com/subject/19971952/

    (转载本站文章请注明作者和出处 Vernon Zheng(郑雪峰) – vernonzheng.com ,请勿用于任何商业用途)

    《快学scala》习题解答-第十三章-集合

    《快学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。

    第十三章 集合

    13.1
    编写一个函数,给定字符串,产出一个包含所有字符的下标的映射。举例来说:indexes(“Mississippi”)应返回一个映射,让’M’对应集{0},’i’对应集{1,4,7,10},依此类推。使用字符到可变集的映射。另外,你如何保证集是经过排序的?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    def mapStrIndex(str:String)={
    var indexMap = new HashMap[Char,SortedSet[Int]]()
    var i = 0
    str.toCharArray.foreach {
    c =>
    indexMap.get(c) match {
    case Some(result) => indexMap(c) = result + i
    case None => indexMap += (c -> SortedSet {
    i
    })
    }
    i += 1
    }
    indexMap
    }
    println(mapStrIndex("Mississippi"))

    13.2
    重复前一个练习,这次用字符到列表的不可变映射。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    mport scala.collection.mutable.{ListBuffer, HashMap}
    def mapStrIndex(str:String)={
    var indexMap = new HashMap[Char,ListBuffer[Int]]()
    var i = 0
    str.toCharArray.foreach {
    c =>
    indexMap.get(c) match {
    case Some(result) => result += i
    case None => indexMap += (c -> ListBuffer {
    i
    })
    }
    i += 1
    }
    indexMap
    }
    println(mapStrIndex("Mississippi"))

    13.3
    编写一个函数,从一个整型链表中去除所有的零值。

    1
    2
    3
    4
    def removeZero(list:List[Int]):List[Int]={
    list.filter(_!=0)
    }
    println(removeZero(List(3,25,0,2,0,0)))

    13.4
    编写一个函数,接受一个字符串的集合,以及一个从字符串到整数值的映射。返回整型的集合,其值为能和集合中某个字符串相对应的映射的值。举例来说,给定Array(“Tom”,”Fred”,”Harry”)和Map(“Tom”->3,”Dick”->4,”Harry”->5),返回Array(3,5)。提示:用flatMap将get返回的Option值组合在一起

    1
    2
    3
    4
    def filterMap(array:Array[String],map:Map[String,Int]):Array[Int]={
    array.flatMap(map.get(_))
    }
    println(filterMap(Array("Tom","Fred","Harry"),Map("Tom"->3,"Dick"->4,"Harry"->5)).mkString(","))

    13.5
    实现一个函数,作用与mkString相同,使用reduceLeft。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    import collection.mutable
    trait MktString{
    this:mutable.Iterable[String]=>
    def mktString(split:String="") = if( this != Nil) this.reduceLeft(_ + split + _)
    }
    var test = new mutable.HashSet[String] with MktString
    test += "1"
    test += "2"
    test += "3"
    println(test.mktString(","))

    13.6
    给定整型列表lst,(lst :\ ListInt)( :: )得到什么?(ListInt /: lst)( :+ )又得到什么?如何修改它们中的一个,以对原列表进行反向排序?

    1
    2
    3
    4
    val lst = List(1,2,3,4,5)
    println((lst :\ List[Int]())(_ :: _))
    println((List[Int]() /: lst)(_ :+ _))
    println((List[Int]() /: lst)((a,b) => b :: a))

    13.7
    在13.11节中,表达式(prices zip quantities) map { p => p.1 * p._2}有些不够优雅。我们不能用(prices zip quantities) map { },因为 _ 是一个带两个参数的函数,而我们需要的是一个带单个类型为元组的参数的函数,Function对象的tupled方法可以将带两个参数的函数改为以元俎为参数的函数。将tupled应用于乘法函数,以使我们可以用它来映射由对偶组成的列表。

    1
    2
    3
    val prices = List(5.0,20.0,9.95)
    val quantities = List(10,2,1)
    println((prices zip quantities) map { Function.tupled(_ * _) })

    13.8
    编写一个函数。将Double数组转换成二维数组。传入列数作为参数。举例来说,Array(1,2,3,4,5,6)和三列,返回Array(Array(1,2,3),Array(4,5,6))。用grouped方法。

    1
    2
    3
    4
    5
    def divArr(arr:Array[Double],i:Int)={
    arr.grouped(i).toArray
    }
    val arr = Array(1.0,2,3,4,5,6)
    divArr(arr,3).foreach(a => println(a.mkString(",")))

    13.9
    Harry Hacker写了一个从命令行接受一系列文件名的程序。对每个文件名,他都启动一个新的线程来读取文件内容并更新一个字母出现频率映射,声明为:
    val frequencies = new scala.collection.multable.HashMap[Char,Int] with scala.collection.mutable.SynchronizedMap[Char,Int]
    当读到字母c时,他调用
    frequencies(c) = frequencies.getOrElse(c,0) + 1
    为什么这样做得不到正确答案?如果他用如下方式实现呢:
    import scala.collection.JavaConversions.asScalaConcurrentMap
    val frequencies:scala.collection.mutable.ConcurrentMap[Char,Int] = new java.util.concurrent.ConcurrentHashMap[Char,Int]

    并发问题,并发修改集合不安全。

    13.10
    Harry Hacker把文件读取到字符串中,然后想对字符串的不同部分用并行集合来并发地更新字母出现频率映射。他用了如下代码:
    val frequencies = new scala.collection.mutable.HashMap[Char,Int]
    for(c <- str.par) frequencies(c) = frequencies.getOrElse(c,0) + 1
    为什么说这个想法很糟糕?要真正地并行化这个计算,他应该怎么做呢?(提示:用aggregate) 并行修改共享变量,结果无法估计。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    import scala.collection.immutable.HashMap
    val str = "abdcsdcd"
    val frequencies = str.par.aggregate(HashMap[Char,Int]())(
    {
    (a,b) =>
    a + (b -> (a.getOrElse(b,0) + 1))
    }
    ,
    {
    (map1,map2) =>
    (map1.keySet ++ map2.keySet).foldLeft( HashMap[Char,Int]() ) {
    (result,k) =>
    result + ( k -> ( map1.getOrElse(k,0 ) + map2.getOrElse(k,0) ) )
    }
    }
    )

    参考:
    《快学Scala》:http://book.douban.com/subject/19971952/

    (转载本站文章请注明作者和出处 Vernon Zheng(郑雪峰) – vernonzheng.com ,请勿用于任何商业用途)

    《快学scala》习题解答-第十二章-高阶函数

    《快学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。

    第十二章 高阶函数

    12.1
    编写函数values(fun:(Int)=>Int,low:Int,high:Int),该函数输出一个集合,对应给定区间内给定函数的输入和输出。比如,values(x=>x*x,-5,5)应该产出一个对偶的集合(-5,25),(-4,16),(-3,9),…,(5,25)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def values(fun:(Int)=>Int,low:Int,high:Int) ={
    var array = List[(Int,Int)]()
    low to high foreach {
    x =>
    array = (x, fun(x)) :: array
    }
    array
    }
    println(values(x => x * x, -5, 5).mkString)

    12.2
    如何用reduceLeft得到数组中的最大元素?

    1
    2
    val arr = Array(1,333,4,6,4,4,9,32,6,9,0,2)
    print(arr.reduceLeft((l,r)=>if(l>=r) l else r))

    12.3
    用to和reduceLeft实现阶乘函数,不得使用循环或递归

    1
    2
    3
    def factorial(n:Int): Unit ={
    1 to n reduceLeft(_ * _)
    }

    12.4
    前一个实现需要处理一个特殊情况,即n<1的情况。展示如何用foldLeft来避免这个需要。

    1
    2
    3
    4
    def factorial(n:Int) = (1 to n).foldLeft(1)(_ * _)
    factorial(-3)
    factorial(3)

    12.5
    编写函数largest(fun:(Int)=>Int,inputs:Seq[Int]),输出在给定输入序列中给定函数的最大值。举例来说,largest(x=>10x-xx,1 to 10)应该返回25.不得使用循环或递归

    1
    2
    3
    4
    5
    6
    def largest1(fun:(Int)=>Int, inputs:Seq[Int]) = inputs.foldLeft(1)((a,b)=> if(fun(b)>a) fun(b) else a)
    def largest2(fun:(Int)=>Int, inputs:Seq[Int]) = inputs.map(fun(_)).max
    println(largest1(x => 10 * x - x * x, 1 to 10))
    println(largest2(x => 10 * x - x * x, 1 to 10))

    12.6
    修改前一个函数,返回最大的输出对应的输入。举例来说,largestAt(fun:(Int)=>Int,inputs:Seq[Int])应该返回5。不得使用循环或递归

    1
    2
    3
    4
    5
    6
    def largestAt1(fun:(Int)=>Int, inputs:Seq[Int]) = inputs.reduce((a,b)=> if(fun(b)>fun(a)) b else a)
    def largestAt2(fun: (Int) => Int, inputs: Seq[Int]) = inputs.map(x => (x, fun(x))).reduceLeft((x,y) => if (x._2 > y._2) x else y)._1
    println(largestAt1(x => 10 * x - x * x, 1 to 10))
    println(largestAt2(x => 10 * x - x * x, 1 to 10))

    12.7
    要得到一个序列的对偶很容易,比如:
    val pairs = (1 to 10) zip (11 to 20)
    假定你想要对这个序列做某中操作—比如,给对偶中的值求和,但是你不能直接使用:

    pairs.map( + )
    函数 + 接受两个Int作为参数,而不是(Int,Int)对偶。编写函数adjustToPair,该函数接受一个类型为(Int,Int)=>Int的函数作为参数,并返回一个等效的, 可以以对偶作为参数的函数。举例来说就是:adjustToPair( * )((6,7))应得到42。然后用这个函数通过map计算出各个对偶的元素之和

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def ajustToPair(fun: (Int, Int) => Int) = (x: (Int, Int)) => fun(x._1, x._2)
    val x = ajustToPair(_ * _)((6, 7))
    println(x)
    val pairs = (1 to 10) zip (11 to 20)
    println(pairs)
    val y = pairs.map(ajustToPair(_ + _))
    println(y)

    12.8
    在12.8节中,你看到了用于两组字符串数组的corresponds方法。做出一个对该方法的调用,让它帮我们判断某个字符串数组里的所有元素的长度是否和某个给定的整数数组相对应

    1
    2
    3
    4
    5
    6
    val a = Array("asd","df","abcd")
    val b = Array(3,2,4)
    val c = Array(3,2,1)
    println(a.corresponds(b)(_.length == _))
    println(a.corresponds(c)(_.length == _))

    12.9
    不使用柯里化实现corresponds。然后尝试从前一个练习的代码来调用。你遇到了什么问题?

    没有柯里化则不能使用前一个练习里的代码方式来调用

    12.10
    实现一个unless控制抽象,工作机制类似if,但条件是反过来的。第一个参数需要是换名调用的参数吗?你需要柯里化吗?

    1
    2
    3
    def unless(condition: => Boolean)(block: => Unit) { if (!condition) { block } }
    unless (0 > 1) { println("Unless!") }

    需要换名和柯里化


    参考:
    《快学Scala》:http://book.douban.com/subject/19971952/

    (转载本站文章请注明作者和出处 Vernon Zheng(郑雪峰) – vernonzheng.com ,请勿用于任何商业用途)