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

第十一章 操作符

11.1
根据优先级规则,3 + 4 -> 5和3 -> 4 + 5是如何被求值的?

1
2
3
4
5
6
7
8
9
scala> 3+4->5
res0: (Int, Int) = (7,5)
scala> 3->4+5
<console>:8: error: type mismatch;
found : Int(5)
required: String
3->4+5
^

11.2
BigInt类有一个pow方法,但没有用操作符字符。Scala类库的设计者为什么没有选用**(像Fortran那样)或者^(像Pascal那样)作为乘方操作符呢?

Scala中的操作符就是方法,其优先级是根据首字母来判断的,优先级如下
最高优先级:除以下字符外的操作符字符

1
2
3
4
5
6
7
8
9
10
* / %
+ -
:
= !
< >
&
ˆ
|
非操作符
最低优先级:赋值操作符

一般乘方的操作符是优于乘法操作的,如果使用*作为乘方的话,那么其优先级则与相同,而如果使用^的话,则优先级低于*操作。优先级都是有问题的。故没有使用这两种操作符

11.3
实现Fraction类,支持+/操作。支持约分,例如将15/-6变为-5/2。除以最大公约数,像这样:
class Fraction(n:Int,d:Int){
private val num:Int = if(d==0) 1 else n
sign(d)/gcd(n,d);
private val den:Int = if(d==0) 0 else d * sign(d)/gcd(n,d);
override def toString = num + “/“ + den
def sign(a:Int) = if(a > 0) 1 else if (a < 0) -1 else 0
def gcd(a:Int,b:Int):Int = if(b==0) abs(a) else gcd(b,a%b)

}

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
mport scala.math.abs
class Fraction(n: Int, d: Int) {
private val num: Int = if (d == 0) 1 else n * sign(d) / gcd(n, d);
private val den: Int = if (d == 0) 0 else d * sign(d) / gcd(n, d);
override def toString = num + "/" + den
def sign(a: Int) = if (a > 0) 1 else if (a < 0) -1 else 0
def gcd(a: Int, b: Int): Int = if (b == 0) abs(a) else gcd(b, a % b)
def +(other:Fraction):Fraction={
newFraction((this.num * other.den) + (other.num * this.den),this.den * other.den)
}
def -(other:Fraction):Fraction={
newFraction((this.num * other.den) - (other.num * this.den),this.den * other.den)
}
def *(other:Fraction):Fraction={
newFraction(this.num * other.num,this.den * other.den)
}
def /(other:Fraction):Fraction={
newFraction(this.num * other.den,this.den * other.num)
}
private def newFraction(a:Int,b:Int):Fraction={
val x:Int = if (b == 0) 1 else a * sign(b) / gcd(a, b)
val y:Int = if (b == 0) 0 else b * sign(b) / gcd(a, b)
new Fraction(x,y)
}
}
object Test extends App{
val f = new Fraction(15,-6)
val p = new Fraction(20,60)
println(f)
println(p)
println(f + p)
println(f - p)
println(f * p)
println(f / p)
}

11.4
实现一个Money类,加入美元和美分字段。提供+,-操作符以及比较操作符==和<。举例来说,Money(1,75)+Money(0,50)==Money(2,25)应为true。你应该同时提供*和/操作符吗?为什么?

不需要提供,金额的乘除没有实际意义。

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
class Money(dollar:Int,cent:Int){
def + (other:Money):Money = {
val (a,b) = (this.cent + other.cent) % 100
new Money(this.dollar + other.dollar + a,b)
}
def -(other:Money):Money={
val (d,c) = (this.toCent() - other.toCent()) % 100
new Money(d,c)
}
def ==(other:Money):Boolean = this.dollar == other.dollar && this.cent == other.cent
def <(other:Money):Boolean = this.dollar < other.dollar || (this.dollar == other.dollar && this.cent < other.cent)
override def toString = "dollar = " + dollar + " cent = " + cent
private def toCent()={
this.dollar * 100 + this.cent
}
}
object Money{
def apply(dollar:Int, cent:Int): Money ={
new Money(dollar,cent)
}
def main(args:Array[String]){
val m1 = Money(1,200)
val m2 = Money(2,2)
println(m1 + m2)
println(m1 - m2)
println(m1 == m2)
println(m1 < m2)
println(Money(1,75)+Money(0,50))
println(Money(1,75)+Money(0,50)==Money(2,25))
}
}

11.5
提供操作符用于构造HTML表格。例如:Table() | “Java” | “Scala” || “Gosling” | “Odersky” || “JVM” | “JVM,.NET”应产出:

Java
Scala
Gosling…

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
class Table{
var s:String = ""
def |(str:String):Table={
val t = Table()
t.s = this.s + "<td>" + str + "</td>"
t
}
def ||(str:String):Table={
val t = Table()
t.s = this.s + "</tr><tr><td>" + str + "</td>"
t
}
override def toString():String={
"<table><tr>" + this.s + "</tr></table>"
}
}
object Table{
def apply():Table={
new Table()
}
def main(args: Array[String]) {
println(Table() | "Java" | "Scala" || "Gosling" | "Odersky" || "JVM" | "JVM,.NET")
}
}

11.6
提供一个ASCIIArt类,其对象包含类似这样的图形:
/_/\
( ‘ ‘ )
( - )
| | |
(|)
提供将两个ASCIIArt图形横向或纵向结合的操作符。选用适当优先级的操作符命名。纵向结合的实例
/_/\ ——-
( ‘ ‘ ) / Hello \
( - ) < Scala |
| | | \ Coder /
(|) ——-

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
import collection.mutable.ArrayBuffer
class ASCIIArt(str:String){
val arr:ArrayBuffer[ArrayBuffer[String]] = new ArrayBuffer[ArrayBuffer[String]]()
if (str != null && !str.trim.eq("")){
str.split("[\r\n]+").foreach{
line =>
val s = new ArrayBuffer[String]()
s += line
arr += s
}
}
def this(){
this("")
}
def +(other:ASCIIArt):ASCIIArt={
val art = new ASCIIArt()
val length = if (this.arr.length >= other.arr.length) this.arr.length else other.arr.length
for(i <- 0 until length){
val s = new ArrayBuffer[String]()
val thisArr:ArrayBuffer[String] = if (i < this.arr.length) this.arr(i) else new ArrayBuffer[String]()
val otherArr:ArrayBuffer[String] = if (i < other.arr.length) other.arr(i) else new ArrayBuffer[String]()
thisArr.foreach(s += _)
otherArr.foreach(s += _)
art.arr += s
}
art
}
def *(other:ASCIIArt):ASCIIArt={
val art = new ASCIIArt()
this.arr.foreach(art.arr += _)
other.arr.foreach(art.arr += _)
art
}
override def toString()={
var ss:String = ""
arr.foreach{
ss += _.mkString(" ") + "\n"
}
ss
}
}
object Test extends App{
val a = new ASCIIArt(""" /\_/\
|( ' ' )
|( - )
| | | |
|(__|__)
|""".stripMargin)
val b = new ASCIIArt( """ -----
| / Hello \
| < Scala |
| \ Coder /
| -----
|""".stripMargin)
println(a + b * b)
println((a + b) * b)
println(a * b)
}

11.7
实现一个BigSequence类,将64个bit的序列打包在一个Long值中。提供apply和update操作来获取和设置某个具体的bit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class BitSequence(private var value: Long = 0) {
implicit def bool2int(b: Boolean) = if (b) 1 else 0
def update(bit: Int, state: Int) = value |= (state & 1L) << bit % 64
def apply(bit: Int): Int = if ((value & 1L << bit % 64) > 0) 1 else 0
override def toString = "%64s".format(value.toBinaryString).replace(" ", "0")
}
val x = new BitSequence()
x(5) = 1
x(63) = 1
x(64) = 1
println(x(5))
println(x)

11.8
提供一个Matrix类—你可以选择需要的是一个22的矩阵,任意大小的正方形矩阵,或mn的矩阵。支持+和操作。操作应同样适用于单值,例如mat*2。单个元素可以通过mat(row,col)得到

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
class Matrix(val m: Int, val n: Int=m) {
private val value = Array.ofDim[Double](m, n)
def update(x: Int, y: Int, v: Double) = value(x)(y) = v
def apply(x: Int, y: Int) = value(x)(y)
def +(other: Matrix) = {
require (n == other.n)
require (m == other.m)
var res = new Matrix(m, n)
for(i <- 0 until m; j <- 0 until n) {
res(i, j) = this.value(i)(j) + other.value(i)(j)
}
res
}
def -(other: Matrix) = this + other * -1
def *(factor: Double) = {
var res = new Matrix(m, n)
for(i <- 0 until m; j <- 0 until n) {
res(i, j) = this.value(i)(j) * factor
}
res
}
private def prod(other: Matrix, i: Int, j: Int) = {
(for (k <- 0 until n) yield value(i)(k) * other.value(j)(k)).sum
}
def *(other: Matrix) = {
require(n == other.m)
var res = new Matrix(m, n)
for(i <- 0 until m; j <- 0 until n) {
res(i, j) = prod(other, i, j)
}
res
}
override def toString = value.map(_.mkString(" ")).mkString("\n")
}
val x = new Matrix(2, 2)
x(0, 0) = 1
x(0, 1) = 2
x(1, 0) = 3
x(1, 1) = 4
println(x)
println()
println(x * 2)
println()
println(x * 2 - x)
println()
println((x * 2) * (x * 3))

11.9
为RichFile类定义unapply操作,提取文件路径,名称和扩展名。举例来说,文件/home/cay/readme.txt的路径为/home/cay,名称为readme,扩展名为txt

1
2
3
4
5
6
7
8
9
10
class RichFile(val path:String) extends java.io.File(path){
}
object RichFile{
def unapply(richFile:RichFile): Unit ={
val path = richFile.path
val pos = path.lastIndexOf("/")
if (pos == -1) None else Some((path.substring(0, pos), path.substring(pos + 1)))
}
}

11.10
为RichFile类定义一个unapplySeq,提取所有路径段。举例来说,对于/home/cay/readme.txt,你应该产出三个路径段的序列:home,cay和readme.txt

1
2
3
4
5
object RichFile {
def unapplySeq(s: String): Option[Seq[String]] = {
if (s.trim == "") None else Some(s.trim.split("/"))
}
}

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

第十章 特质

10.1
java.awt.Rectangle类有两个很有用的方法translate和grow,但可惜的是像java.awt.geom.Ellipse2D这样的类没有。在Scala中,你可以解决掉这个问题。定义一个RenctangleLike特质,加入具体的translate和grow方法。提供任何你需要用来实现的抽象方法,以便你可以像如下代码这样混入该特质:
val egg = new java.awt.geom.Ellipse2D.Double(5,10,20,30) with RectangleLike
egg.translate(10,-10)
egg.grow(10,20)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.awt.geom.Ellipse2D
trait RectangleLike{
this:Ellipse2D.Double=>
def translate(x:Double,y:Double){
this.x = x
this.y = y
}
def grow(x:Double,y:Double){
this.x += x
this.y += y
}
}
object Test extends App{
val egg = new Ellipse2D.Double(5,10,20,30) with RectangleLike
println("x = " + egg.getX + " y = " + egg.getY)
egg.translate(10,-10)
println("x = " + egg.getX + " y = " + egg.getY)
egg.grow(10,20)
println("x = " + egg.getX + " y = " + egg.getY)
}

10.2
通过把scala.math.Ordered[Point]混入java.awt.Point的方式,定义OrderedPoint类。按辞典编辑方式排序,也就是说,如果x<x’或者x=x’且y<y’则(x,y)<(x’,y’)

1
2
3
4
5
6
7
8
import java.awt.Point
class OrderedPoint extends Point with Ordered[Point]{
override def compare(that: Point): Int = {
if (this.x <= that.x && this.y < that.y) -1
else if (this.x == that.x && this.y == that.y) 0
else 1
}
}

10.3
查看BitSet类,将它的所有超类和特质绘制成一张图。忽略类型参数([…]中的所有内容)。然后给出该特质的线性化规格说明

10.4
提供一个CryptoLogger类,将日志消息以凯撒密码加密。缺省情况下密匙为3,不过使用者也可以重写它。提供缺省密匙和-3作为密匙是的使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
trait Logger{
def log(str:String,key:Int = 3):String
}
class CryptoLogger extends Logger{
def log(str: String, key:Int): String = {
for ( i <- str) yield if (key >= 0) (97 + ((i - 97 + key)%26)).toChar else (97 + ((i - 97 + 26 + key)%26)).toChar
}
}
object Test extends App{
val plain = "chenzhen";
println("明文为:" + plain);
println("加密后为:" + new CryptoLogger().log(plain));
println("加密后为:" + new CryptoLogger().log(plain,-3));
}

10.5
JavaBean规范里有一种提法叫做属性变更监听器(property change listener),这是bean用来通知其属性变更的标准方式。PropertyChangeSupport类对于任何想要支持属性变更通知其属性变更监听器的bean而言是个便捷的超类。但可惜已有其他超类的类—比如JComponent—必须重新实现相应的方法。将PropertyChangeSupport重新实现为一个特质,然后将它混入到java.awt.Point类中

1
2
3
4
5
6
import java.awt.Point
import java.beans.PropertyChangeSupport
trait PropertyChange extends PropertyChangeSupport
val p = new Point() with PropertyChange

10.6
在Java AWT类库中,我们有一个Container类,一个可以用于各种组件的Component子类。举例来说,Button是一个Component,但Panel是Container。这是一个运转中的组合模式。Swing有JComponent和JContainer,但如果你仔细看的话,你会发现一些奇怪的细节。尽管把其他组件添加到比如JButton中毫无意义,JComponent依然扩展自Container。Swing的设计者们理想情况下应该会更倾向于图10-4中的设计。但在Java中那是不可能的。请解释这是为什么?Scala中如何用特质来设计出这样的效果?

Java只能单继承。

10.7
市面上有不下数十种关于Scala特质的教程,用的都是些”在叫的狗”啦,”讲哲学的青蛙”啦之类的傻乎乎的例子。阅读和理解这些机巧的继承层级很乏味且对于理解问题没什么帮助,但自己设计一套继承层级就不同了,会很有启发。做一个你自己的关于特质的继承层级,要求体现出叠加在一起的特质,具体的和抽象的方法,以及具体的和抽象的字段

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
trait Fly{
def fly(){
println("flying")
}
def flywithnowing()
}
trait Walk{
def walk(){
println("walk")
}
}
class Bird{
var name:String = _
}
class BlueBird extends Bird with Fly with Walk{
def flywithnowing() {
println("BlueBird flywithnowing")
}
}
object Test extends App{
val b = new BlueBird()
b.walk()
b.flywithnowing()
b.fly()
}

10.8
在java.io类库中,你可以通过BufferedInputStream修饰器来给输入流增加缓冲机制。用特质来重新实现缓冲。简单起见,重写read方法

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
import java.io.{InputStream, FileInputStream}
trait Buffering {
this: InputStream =>
val BUF_SIZE: Int = 5
private val buf = new Array[Byte](BUF_SIZE)
private var bufsize: Int = 0
private var pos: Int = 0
override def read(): Int = {
if (pos >= bufsize) {
bufsize = this.read(buf, 0, BUF_SIZE)
if (bufsize > 0) -1
pos = 0
}
pos += 1
buf(pos-1)
}
}
val f = new FileInputStream("08.txt") with Buffering
for(i <- 1 to 10) println(f.read())

10.9
使用本章的日志生成器特质,给前一个练习中的方案增加日志功能,要求体现缓冲的效果

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
import java.io.{InputStream, FileInputStream}
trait Logger {
def log(msg: String)
}
trait NoneLogger extends Logger {
def log(msg: String) = {}
}
trait PrintLogger extends Logger {
def log(msg: String) = println(msg)
}
trait Buffering {
this: InputStream with Logger =>
val BUF_SIZE: Int = 5
private val buf = new Array[Byte](BUF_SIZE)
private var bufsize: Int = 0
private var pos: Int = 0
override def read(): Int = {
if (pos >= bufsize) {
bufsize = this.read(buf, 0, BUF_SIZE)
log("buffered %d bytes: %s".format(bufsize, buf.mkString(", ")))
if (bufsize > 0) -1
pos = 0
}
pos += 1
buf(pos-1)
}
}
val f = new FileInputStream("exercise08.txt") with Buffering with PrintLogger
for(i <- 1 to 10) println(f.read())

10.10
实现一个IterableInputStream类,扩展java.io.InputStream并混入Iterable[Byte]特质

1
2
3
4
5
6
7
8
9
10
class IterableInputStream extends java.io.InputStream with Iterable[Byte]{
class InputStreamIterator(outer: IterableInputStream) extends Iterator[Byte] {
def hasNext: Boolean = outer.available() > 0
def next: Byte = outer.read().toByte
}
override def iterator: Iterator[Byte] = new InputStreamIterator(this)
override def read(): Int = 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。

第九章 文件和正则表达式

9.1
编写一小段Scala代码,将某个文件中的行倒转顺序(将最后一行作为第一行,依此类推)

1
2
3
4
5
6
val path = "./exercise01.txt"
val file = Source.fromFile(path)
val reverseLines = file.getLines().toArray.reverse
val pw = new PrintWriter(path)
reverseLines.foreach (line => pw.write(line+"\n"))
pw.close()

9.2
编写Scala程序,从一个带有制表符的文件读取内容,将每个制表符替换成一组空格,使得制表符隔开的n列仍然保持纵向对齐,并将结果写入同一个文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import io.Source
import java.io.PrintWriter
val path = "test.txt"
val reader = Source.fromFile(path).getLines()
val result = for ( t <- reader) yield t.replaceAll("\\t"," ")
val pw = new PrintWriter(path)
result.foreach(line => pw.write(line + "\n"))
pw.close()

9.3
编写一小段Scala代码,从一个文件读取内容并把所有字符数大于12的单词打印到控制台。如果你能用单行代码完成会有额外奖励

1
scala.io.Source.fromFile("./exercise01.txt").mkString.split("\\s+").foreach(line=> if(line.length>12) println(line))

9.4
编写Scala程序,从包含浮点数的文本文件读取内容,打印出文件中所有浮点数之和,平均值,最大值和最小值

1
2
3
4
5
6
7
8
9
10
11
import scala.io.Source
val nums = Source.fromFile("exercise01.txt").mkString.split("\\s+")
var total = 0d
nums.foreach(total += _.toDouble)
println(total)
println(total/nums.length)
println(nums.max)
println(nums.min)

9.5
编写Scala程序,向文件中写入2的n次方及其倒数,指数n从0到20。对齐各列:
1 1
2 0.5
4 0.25
… …

1
2
3
4
5
6
7
8
9
10
11
12
13
import java.io.PrintWriter
val pw = new PrintWriter("exercise01.txt")
for ( n <- 0 to 20){
val t = BigDecimal(2).pow(n)
pw.write(t.toString())
pw.write("\t\t")
pw.write((1/t).toString())
pw.write("\n")
}
pw.close()

9.6
编写正则表达式,匹配Java或C++程序代码中类似”like this,maybe with \” or\“这样的带引号的字符串。编写Scala程序将某个源文件中所有类似的字符串打印出来

1
2
3
4
5
val source = scala.io.Source.fromFile("test.txt").mkString
val pattern = """"([^"\\]*([\\]+"[^"\\]*)*)"""".r
pattern.findAllIn(source).foreach(println)

9.7
编写Scala程序,从文本文件读取内容,并打印出所有的非浮点数的词法单位。要求使用正则表达式

1
2
3
4
5
6
7
import io.Source
val source = Source.fromFile("test.txt").mkString
val pattern = """[^((\d+\.){0,1}\d+)^\s+]+""".r
pattern.findAllIn(source).foreach(println)

9.8
编写Scala程序打印出某个网页中所有img标签的src属性。使用正则表达式和分组

1
2
3
4
val pattern = """<img[^>]+(src\s*=\s*"[^>^"]+")[^>]*>""".r
val source = scala.io.Source.fromURL("http://www.vernonzheng.com","utf-8").mkString
for (pattern(str) <- pattern.findAllIn(source)) println(str)

9.9
编写Scala程序,盘点给定目录及其子目录中总共有多少以.class为扩展名的文件

1
2
3
4
5
6
7
def countClass(dir:java.io.File): Int = {
var num:Int = 0
val files = dir.listFiles()
num += files.filter(_.isFile).count(_.getName.endsWith(".class"))
files.filter(_.isDirectory).foreach(num += countClass(_))
num
}

9.10
扩展那个可序列化的Person类,让它能以一个集合保存某个人的朋友信息。构造出一些Person对象,让他们中的一些人成为朋友,然后将Array[Person]保存到文件。将这个数组从文件中重新读出来,校验朋友关系是否完好

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
mport collection.mutable.ArrayBuffer
import java.io.{ObjectInputStream, FileOutputStream, FileInputStream, ObjectOutputStream}
class Person(var name:String) extends Serializable{
val friends = new ArrayBuffer[Person]()
def addFriend(friend : Person){
friends += friend
}
override def toString() = {
var str = "My name is " + name + " and my friends name is "
friends.foreach(str += _.name + ",")
str
}
}
object Test extends App{
val p1 = new Person("Ivan")
val p2 = new Person("F2")
val p3 = new Person("F3")
p1.addFriend(p2)
p1.addFriend(p3)
println(p1)
val out = new ObjectOutputStream(new FileOutputStream("person.obj"))
out.writeObject(p1)
out.close()
val in = new ObjectInputStream(new FileInputStream("person.obj"))
val p = in.readObject().asInstanceOf[Person]
println(p)
}

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

第八章 继承

8.1
扩展如下的BankAccount类,新类CheckingAccount对每次存款和取款都收取1美元的手续费
class BankAccount(initialBalance:Double){
private var balance = initialBalance
def deposit(amount:Double) = { balance += amount; balance}
def withdraw(amount:Double) = {balance -= amount; balance}
}

1
2
3
4
5
6
7
8
9
10
class BankAccount(initialBalance:Double){
private var balance = initialBalance
def deposit(amount:Double) = { balance += amount; balance}
def withdraw(amount:Double) = {balance -= amount; balance}
}
class CheckingAccount(initialBanlance:Double) extends BankAccount(initialBanlance){
override def deposit(amount:Double) = super.deposit(amount-1)
override def withdraw(amount:Double) = super.withdraw(amount+1)
}

8.2
扩展前一个练习的BankAccount类,新类SavingsAccount每个月都有利息产生(earnMonthlyInterest方法被调用),并且有每月三次免手续费的存款或取款。在earnMonthlyInterest方法中重置交易计数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class BankAccount(initialBalance:Double){
private var balance = initialBalance
def deposit(amount:Double) = { balance += amount; balance}
def withdraw(amount:Double) = {balance -= amount; balance}
}
class SavingsAccount(initialBalance:Double) extends BankAccount(initialBalance){
private var num:Int = _
def earnMonthlyInterest()={
num = 3
super.deposit(1)
}
override def deposit(amount: Double): Double = {
num -= 1
if(num < 0) super.deposit(amount - 1) else super.deposit(amount)
}
override def withdraw(amount: Double): Double = {
num -= 1
if (num < 0) super.withdraw(amount + 1) else super.withdraw(amount)
}
}

8.3
翻开你喜欢的Java或C++教科书,一定会找到用来讲解继承层级的实例,可能是员工,宠物,图形或类似的东西。用Scala来实现这个示例。

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
/**
* java
* class Art{
Art(){System.out.println("Art constructor");}
}
class Drawing extends Art{
Drawing() {System.out.println("Drawing constructor");}
}
public class Cartoon extends Drawing{
public Cartoon() { System.out.println("Cartoon constructor");}
}
*/
class Art{
println("Art constructor")
}
class Drawing extends Art{
println("Drawing constructor")
}
class Cartoon extends Drawing{
println("Cartoon constructor")
}

8.4
定义一个抽象类Item,加入方法price和description。SimpleItem是一个在构造器中给出价格和描述的物件。利用val可以重写def这个事实。Bundle是一个可以包含其他物件的物件。其价格是打包中所有物件的价格之和。同时提供一个将物件添加到打包当中的机制,以及一个适合的description方法

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
import collection.mutable.ArrayBuffer
abstract class Item{
def price():Double
def description():String
override def toString():String={
"description:" + description() + " price:" + price()
}
}
class SimpleItem(val price:Double,val description:String) extends Item{
}
class Bundle extends Item{
val items = new ArrayBuffer[Item]()
def addItem(item:Item){
items += item
}
def price(): Double = {
var total = 0d
items.foreach(total += _.price())
total
}
def description(): String = {
items.mkString(" ")
}
}

8.5
设计一个Point类,其x和y坐标可以通过构造器提供。提供一个子类LabeledPoint,其构造器接受一个标签值和x,y坐标,比如:new LabeledPoint(“Black Thursday”,1929,230.07)

1
2
class Point(x:Double, y:Double)
class LabeledPoint(x:Double, y:Double, tag:String) extends Point(x,y)

8.6
定义一个抽象类Shape,一个抽象方法centerPoint,以及该抽象类的子类Rectangle和Circle。为子类提供合适的构造器,并重写centerPoint方法

1
2
3
4
5
6
7
8
9
10
11
abstract class Shape{
def centerPoint()
}
class Rectangle(startX:Int,startY:Int,endX:Int,endY:Int) extends Shape{
def centerPoint() {}
}
class Circle(x:Int,y:Int,radius:Double) extends Shape{
def centerPoint() {}
}

8.7
提供一个Square类,扩展自java.awt.Rectangle并且是三个构造器:一个以给定的端点和宽度构造正方形,一个以(0,0)为端点和给定的宽度构造正方形,一个以(0,0)为端点,0为宽度构造正方形

1
2
3
4
5
6
7
8
9
10
11
12
import java.awt.{Point, Rectangle}
class Square(point:Point,width:Int) extends Rectangle(point.x,point.y,width,width){
def this(){
this(new Point(0,0),0)
}
def this(width:Int){
this(new Point(0,0),width)
}
}

8.8
编译8.6节中的Person和SecretAgent类并使用javap分析类文件。总共有多少name的getter方法?它们分别取什么值?(提示:可以使用-c和-private选项)

总共两个。Person中取得的是传入的name,而SecretAgent中取得的是默认的”secret”

8.9
在8.10节的Creature类中,将val range替换成一个def。如果你在Ant子类中也用def的话会有什么效果?如果在子类中使用val又会有什么效果?为什么?

在Ant中使用def没有问题。但是如果使用val则无法编译。因为val只能重写不带参数的def。这里的def是带参数的

8.10
文件scala/collection/immutable/Stack.scala包含如下定义:
class Stack[A] protected (protected val elems: List[A])
请解释protected关键字的含义。(提示:回顾我们在第5章中关于私有构造器的讨论) 此构造方法只能被其子类来调用,而不能被外界直接调用

此构造方法只能被其子类来调用,而不能被外界直接调用


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

第七章 包和引用

7.1
编写示例程序,展示为什么
package com.horstmann.impatient
不同于
package com
package horstmann
package impatient

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com {
class T1() {}
package horstmann {
class T2(t: T1) {}
package impatient {
class T3(t1: T1, t2: T2) {}
}
}
}
package com.horstmann.impatient{
//class T4(t1:T1,t3:T3) //can not find type T1
}

7.2
编写一段让你的Scala朋友们感到困惑的代码,使用一个不在顶部的com包

1
2
3
4
5
6
7
8
9
10
11
12
package vernon {
class T1() {}
package com {
class T2(t: T1) {}
}
package impatient {
class T3(t1: T1, t2: com.T2) {}
}
}

7.3
编写一个包random,加入函数nextInt():Int,nextDouble():Double,setSeed(seed:Int):Unit。生成随机数的算法采用线性同余生成器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package object random{
var seed:Int = _
val a = BigDecimal(1664525)
val b = BigDecimal(1013904223)
val n = 32
def nextInt():Int={
val temp = (seed * a + b) % BigDecimal(2).pow(n)
seed = temp.toInt
seed
}
def nextDouble():Double={
val temp = (seed * a + b) % BigDecimal(2).pow(n)
seed = temp.toInt
temp.toDouble
}
}
import random._
object Q3 extends App {
(1 to 10).foreach(x => println(nextInt()))
(1 to 10).foreach(x => println(nextDouble()))
}

7.4
在你看来Scala的设计者为什么要提供package object语法而不是简单的让你将函数和变量添加到包中呢?

JVM不支持

7.5
private[com] def giveRaise(rate:Double)的含义是什么?有用吗?

除了com包可访问giveRaise,其他包都不能访问。有用。

7.6
编写一段程序,将Java哈希映射中的所有元素拷贝到Scala哈希映射。用引入语句重命名这两个类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
object Q6 extends App{
import java.util.{HashMap => JavaHashMap}
import collection.mutable.{HashMap => ScalaHashMap, Map => ScalaMap}
val javaMap = new JavaHashMap[Int,String]
javaMap.put(1, "One");
javaMap.put(2, "Two");
javaMap.put(3, "Three");
javaMap.put(4, "Four");
val scalaMap = new ScalaHashMap[Int,String]
for(key <- javaMap.keySet().toArray){
scalaMap += (key.asInstanceOf[Int] -> javaMap.get(key))
}
println(scalaMap.mkString(" "))
}

7.7
在前一个练习中,将所有引入语句移动到尽可能小的作用域里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
object Q7 extends App{
import java.util.{HashMap => JavaHashMap}
val javaMap = new JavaHashMap[Int,String]
javaMap.put(1, "One");
javaMap.put(2, "Two");
javaMap.put(3, "Three");
javaMap.put(4, "Four");
import collection.mutable.{HashMap => ScalaHashMap, Map => ScalaMap}
val scalaMap = new ScalaHashMap[Int,String]
for(key <- javaMap.keySet().toArray()){
scalaMap += (key.asInstanceOf[Int] -> javaMap.get(key))
}
println(scalaMap.mkString(" "))
}

7.8
以下代码的作用是什么?这是个好主意吗?
import java.
import javax.

导入java和javax下的所有类。而java和javax下是没有类的。所以此代码无用

7.9
编写一段程序,引入java.lang.System类,从user.name系统属性读取用户名,从Console对象读取一个密码,如果密码不是”secret”,则在标准错误流中打印一个消息;如果密码是”secret”,则在标准输出流中打印一个问候消息。不要使用任何其他引入,也不要使用任何限定词(带句点的那种)

1
2
3
4
5
6
7
8
9
object Q9 extends App{
import java.lang.System._
var password = Console.readLine()
if (password equals "secret")
System.out.println("Hello " + getProperty("user.name"))
else
System.err.println("password error!")
}

7.10
除了StringBuilder,还有哪些java.lang的成员是被scala包覆盖的?

比对java.lang下的类和scala包下的类,略


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

第六章 对象

6.1
编写一个 Conversions 对象,加入 inchesToCentimeters,gallonsToLiters 和 milesToKilometers 方法

1
2
3
4
5
object Conversions{
def inchesToSantimeters(value: Double) = value * 2.54
def gallonsToLiters(value: Double) = value * 3.78541178
def milesToKilometers(value: Double) = value * 1.609344
}

6.2
前一个练习不是很面向对象。提供一个通用的超类 UnitConversion并定义扩展该超类的 InchesToCentimeters,GallonsToLiters 和 MilesToKilometers 对象

1
2
3
4
5
6
7
class UnitConversion(val factor: Double) {
def convert(value: Double): Double = factor * value
}
object InchesToSantimeters extends UnitConversion(2.54)
object GallonsToLiters extends UnitConversion(3.78541178)
object MilesToKilometers extends UnitConversion(1.609344)

6.3
定义一个扩展自 java.awt.Point 的 Origin 对象。为什么说这实际上不是个好主意?(仔细看 Point 类的方法)Point 中的 getLocation 方法返回的是 Point 对象,如果想返回 Origin 对象,需要 Origin 类才行

1
2
3
4
5
import java.awt.Point
object Origin extends Point{
override def getLocation : Point = super.getLocation
}

6.4
定义一个 Point 类和一个伴生对象,使得我们可以不用 new 而直接用 Point(3,4)来构造 Point 实例 apply 方法的使用

1
2
3
4
5
class Point(x: Int = 0, y: Int = 0) extends java.awt.Point(x, y)
object Point {
def apply(x: Int = 0, y: Int = 0) = new Point(x, y)
}

6.5
编写一个 Scala 应用程序,使用 App 特质,以反序打印命令行参数,用空格隔开。举例来说,scala Reverse Hello World 应该打印 World Hello

1
2
3
object Reverse extends App{
args.reverse.mkString(" ")
}

6.6
编写一个扑克牌 4 种花色的枚举,让其 toString 方法分别返回♣,♦,♥,♠

1
2
3
4
5
6
7
8
9
10
object Suits extends Enumeration{
type Suits = Value
val Spade = Value("♠")
val Club = Value("♣")
val Heart = Value("♥")
val Diamond = Value("♦")
override def toString():String={
Suits.values.mkString(",")
}
}

6.7
实现一个函数,检查某张牌的花色是否为红色

1
2
3
4
5
6
7
8
object Suits extends Enumeration {
type Suits = Value
val Spade = Value("♠")
val Club = Value("♣")
val Heart = Value("♥")
val Diamond = Value("♦")
def isRed(card: Suits) = card == Heart || card == Diamond
}

6.8
编写一个枚举,描述 RGB 立方体的 8 个角。ID 使用颜色值(例如 :红色是 0xff0000)

1
2
3
4
5
6
7
8
9
10
11
12
object RGBCube extends Enumeration {
val black = Value(0x000000, "Black")
val red = Value(0xff0000, "Red")
val green = Value(0x00ff00, "Green")
val blue = Value(0x0000ff, "Blue")
val yellow = Value(0xffff00, "Yellow")
val magenta = Value(0xff00ff, "Magenta")
val cyan = Value(0x00ffff, "Cyan")
val white = Value(0xffffff, "White")
}
for( c <- RGBCube.values ) println("0x%06x: %s".format(c.id, c))

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

第五章 类

5.1
改进5.1节的Counter类,让它不要在Int.MaxValue时变成负数

答:

1
2
3
4
5
class Counter(){
private var value = 0
def increment(){ if(value<Int.MaxValue) value += 1 }
def current() = value
}

5.2
编写一个BankAccount类,加入deposit和withdraw方法,和一个只读的balance属性

答:

1
2
3
4
5
class BankAccount{
private val banlance = 0
def deposit(){}
def withdraw(){}
}

5.3
编写一个Time类,加入只读属性hours和minutes,和一个检查某一时刻是否早于另一时刻的方法before(other:Time):Boolean。Time对象应该以new Time(hrs,min)方式构建。其中hrs以军用时间格式呈现(介于0和23之间)

答:

1
2
3
4
5
6
7
8
9
10
11
12
class Time(private[this] val hrs:Int,private[this] val min:Int){
val hours = hrs
val minutes = min
def before(other:Time):Boolean = {
if(hours<other.hours)
true
if(hours==other.hours)
if(minutes<other.minutes)
true
false
}
}

5.4
重新实现前一个类中的Time类,将内部呈现改成午夜起的分钟数(介于0到24*60-1之间)。不要改变公有接口。也就是说,客户端代码不应因你的修改而受影响

答:

1
2
3
4
5
6
7
8
class Time(private[this] val hrs:Int,private[this] val min:Int){
val minutesInDay = hrs*24+min
def before(other:Time):Boolean = {
if(minutesInDay<other.minutesInDay)
true
false
}
}

5.5
创建一个Student类,加入可读写的JavaBeans属性name(类型为String)和id(类型为Long)。有哪些方法被生产?(用javap查看。)你可以在Scala中调用JavaBeans的getter和setter方法吗?应该这样做吗?

答:

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
import scala.beans.BeanProperty
class Student{
@BeanProperty var name:String = _
@BeanProperty var id:Long = _
}
/**
javap -c Student 后显示如下
Compiled from "Student.scala"
public class Student extends java.lang.Object implements scala.ScalaObject{
public java.lang.String name();
Code:
0: aload_0
1: getfield #13; //Field name:Ljava/lang/String;
4: areturn
public void name_$eq(java.lang.String);
Code:
0: aload_0
1: aload_1
2: putfield #13; //Field name:Ljava/lang/String;
5: return
public void setName(java.lang.String);
Code:
0: aload_0
1: aload_1
2: putfield #13; //Field name:Ljava/lang/String;
5: return
public long id();
Code:
0: aload_0
1: getfield #19; //Field id:J
4: lreturn
public void id_$eq(long);
Code:
0: aload_0
1: lload_1
2: putfield #19; //Field id:J
5: return
public void setId(long);
Code:
0: aload_0
1: lload_1
2: putfield #19; //Field id:J
5: return
public long getId();
Code:
0: aload_0
1: invokevirtual #25; //Method id:()J
4: lreturn
public java.lang.String getName();
Code:
0: aload_0
1: invokevirtual #28; //Method name:()Ljava/lang/String;
4: areturn
public Student();
Code:
0: aload_0
1: invokespecial #34; //Method java/lang/Object."<init>":()V
4: return
}
**/

5.6
在5.2节的Person类中提供一个主构造器,将负年龄转换为0

答:

1
2
3
class Person(var age:Int){
age = if(age < 0) 0 else age
}

5.7
编写一个Person类,其主构造器接受一个字符串,该字符串包含名字,空格和姓,如new Person(“Fred Smith”)。提供只读属性firstName和lastName。主构造器参数应该是var,val还是普通参数?为什么?

答:

1
2
3
4
5
class Person(private[this] val name:String){
private[this] val tmp = name.split("\\s+")
val firstName = tmp(0)
val lastName = tmp(1)
}

5.8
创建一个Car类,以只读属性对应制造商,型号名称,型号年份以及一个可读写的属性用于车牌。提供四组构造器。每个构造器fc都要求制造商和型号为必填。型号年份和车牌可选,如果未填,则型号年份为-1,车牌为空串。你会选择哪一个作为你的主构造器?为什么?

答:

1
class Car(val manufactuer:String, val model: String, val year: Int = -1, var license: String = "")

5.9
在Java,C#或C++重做前一个练习。Scala相比之下精简多少?

答:

5.10
考虑如下的类

class Employ(val name:String,var salary:Double){
def this(){this(“John Q. Public”,0.0)}
}
重写该类,使用显示的字段定义,和一个缺省主构造器。你更倾向于使用哪种形式?为什么?

答:

1
2
3
4
class Employ(){
val name:String = "John Q. Public"
var salary:Double = 0.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。

第四章 映射和元组

4.1
设置一个映射,其中包含你想要的一些装备,以及它们的价格。然后构建另一个映射,采用同一组键,但是价格上打9折

答:

1
2
3
4
def buildEquipMap() :Map[String,Double] = {
val equipMap:Map[String,Double] = Map("eq1"->11.1,"eq2"->12.2,"eq3"->14)
for ((k,v) <- equipMap) yield (k,v*0.9)
}

4.2
编写一段程序,从文件中读取单词。用一个可变映射来清点每个单词出现的频率。读取这些单词的操作可以使用java.util.Scanner

答:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import scala.collection.mutable
import scala.io.Source
def countWord() : Unit = {
val wordMap = new mutable.HashMap[String,Int]
val in = new java.util.Scanner(new java.io.File("exercise02.txt"))
while(in.hasNext){
val word = in.next()
wordMap(word) = wordMap.getOrElse(word,0) + 1
}
println(wordMap.mkString(","))
}
def countWord2() : Unit = {
val source = Source.fromFile("exercise02.txt").mkString
val tokens = source.split("\\s+")
val wordMap = new mutable.HashMap[String,Int]
for(word <- tokens){
wordMap(word) = wordMap.getOrElse(word,0) + 1
}
println(wordMap.mkString(","))
}

4.3
重复前一个练习,这次用不可变的映射

答:

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
mport scala.collection.{mutable, immutable}
import scala.io.Source
def countWord() : Unit = {
val source = Source.fromFile("exercise02.txt").mkString
val tokens = source.split("\\s+")
var wordMap = new immutable.HashMap[String,Int]
for(key <- tokens){
wordMap += (key -> (wordMap.getOrElse(key,0) + 1))
}
println(wordMap.mkString(","))
}
/**
* 书中:immutable map += 不断生成新的map,由于都是immutable,共享大部分数据结构,
* 测试下性能
*/
def countWord1(tokens:Array[String]) : Unit = {
val wordMap = new mutable.HashMap[String,Int]
for(word <- tokens){
wordMap(word) = wordMap.getOrElse(word,0) + 1
}
}
def countWord2(tokens:Array[String]) : Unit = {
val wordMap = new mutable.HashMap[String,Int]
for(word <- tokens){
wordMap(word) = wordMap.getOrElse(word,0) + 1
}
}
def performance():Unit={
val source = Source.fromFile("exercise02.txt").mkString
val tokens = source.split("\\s+")
val startTime1 = System.currentTimeMillis()
for(i <- 0 to 10000) {
countWord1(tokens)
}
val endTime1 = System.currentTimeMillis()
val startTime2 = System.currentTimeMillis()
for(i <- 0 to 10000) {
countWord2(tokens)
}
val endTime2 = System.currentTimeMillis()
println("countWord1(mutable map) cost:"+(endTime1-startTime1))
println("countWord2(immutable map) cost:"+(endTime2-startTime2))
/**
* scala> performance
countWord1(mutable map) cost:36
countWord2(immutable map) cost:34
scala> performance
countWord1(mutable map) cost:4
countWord2(immutable map) cost:4
scala> performance
countWord1(mutable map) cost:8
countWord2(immutable map) cost:6
scala> performance
countWord1(mutable map) cost:4
countWord2(immutable map) cost:8
scala> performance
countWord1(mutable map) cost:5
countWord2(immutable map) cost:4
*/
}

4.4
重复前一个练习,这次使用已排序的映射,以便单词可以按顺序打印出来

答:

1
2
3
4
5
6
7
8
9
10
11
12
import scala.collection.immutable.TreeMap
import scala.io.Source
def countWord() : Unit = {
val source = Source.fromFile("exercise02.txt").mkString
val tokens = source.split("\\s+")
var wordMap = new TreeMap[String,Int]
for(key <- tokens){
wordMap += (key -> (wordMap.getOrElse(key,0) + 1))
}
println(wordMap.mkString(","))
}

4.5
重复前一个练习,这次使用java.util.TreeMap并使之适用于Scala API

答:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.util
import scala.collection.mutable.Map
import scala.collection.JavaConversions.mapAsScalaMap
import scala.io.Source
def countWord() : Unit = {
val source = Source.fromFile("exercise02.txt").mkString
val tokens = source.split("\\s+")
var wordMap:Map[String,Int] = new util.TreeMap[String,Int]
for(key <- tokens){
wordMap += (key -> (wordMap.getOrElse(key,0) + 1))
}
println(wordMap.mkString(","))
}

4.6
定义一个链式哈希映射,将”Monday”映射到java.util.Calendar.MONDAY,依次类推加入其他日期。展示元素是以插入的顺序被访问的

答:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.util.Calendar
import scala.collection.mutable
def calendarDays():Unit = {
val daysMap = mutable.LinkedHashMap(
"Monday" -> Calendar.MONDAY,
"Tuesday" -> Calendar.TUESDAY,
"Wednesday" -> Calendar.WEDNESDAY,
"Thursday" -> Calendar.THURSDAY,
"Friday" -> Calendar.FRIDAY,
"Saturday" -> Calendar.SATURDAY,
"Sunday" -> Calendar.SUNDAY
)
println(daysMap.mkString(","))
}

4.7
打印出所有Java系统属性的表格

答:

1
2
3
4
5
6
7
import scala.collection.JavaConversions.propertiesAsScalaMap
def printJavaSysProps():Unit = {
val propMap:collection.Map[String,String] = System.getProperties()
val maxKeyLength = propMap.keySet.map(_.length).max
for( (k,v) <- propMap ) printf("%-" + maxKeyLength + "s | %s\n", k, v)
}

4.8
编写一个函数minmax(values:Array[Int]),返回数组中最小值和最大值的对偶

答:

1
2
3
def minmax(values:Array[Int])={
(values.max,values.min)
}

4.9
编写一个函数Iteqgt(values:Array[int],v:Int),返回数组中小于v,等于v和大于v的数量,要求三个值一起返回

答:

1
2
3
def Iteqgt(values:Array[Int],v:Int){
(values.count(_ < v),values.count(_ == v),values.count(_ > v))
}

4.10
当你将两个字符串拉链在一起,比如”Hello”.zip(“World”),会是什么结果?想出一个讲得通的用例

答:

1
2
scala> "Hello".zip("hello").toMap
res40: scala.collection.immutable.Map[Char,Char] = Map(H -> h, e -> e, l -> l, o -> o)

StringOps中的zip定义如下:

abstract def zipB: StringOps[(A, B)]
GenIterable是可遍历对象需要包含的trait,对于String来说,它是可遍历的。但是它的遍历是遍历单个字母。 所以拉链就针对每个字母来进行。


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

第三章 数组相关操作

3.1
编写一段代码,将a设置为一个n个随机整数的数组,要求随机数介于0(包含)和n(不包含)之间

答:

1
2
3
4
5
6
7
8
9
10
11
import scala.util.Random
import scala.math.random
def randomArray(n: Int) :Array[Int] = {
val array = for( i<- 0 to n) yield Random.nextInt(n)
array.toArray
}
def randomArray2(n: Int) :Array[Int] = {
val array = for( i<- 0 to n) yield (random * n).toInt
array.toArray
}

3.2
编写一个循环,将整数数组中相邻的元素置换。例如,Array(1,2,3,4,5)经过置换后变为Array(2,1,4,3,5)

答:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def arrayChange(array: Array[Int]) :Array[Int] = {
if(array == null)
return null
for(i <- 0 until array.length if i % 2 ==0 && i > 0){
val tmp = array(i-1)
array(i-1) = array(i)
array(i) = tmp
}
array
}
def arrayChange2(arr: Array[Int]) :Array[Int] = {
val t = arr.toBuffer
for(i <- 0 until (t.length,2) if i + 1 < t.length){
val a = t(i)
val b = t(i + 1)
t.remove(i,2)
t.insert(i,b)
t.insert(i + 1,a)
}
t.toArray
}

3.3
重复前一个练习,不过这一次生成一个新的值交换过的数组。用for/yield

答:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def arrayChange(array: Array[Int]) :Array[Int] = {
if(array == null)
return null
val length = array.length
var flag = true
val newArray = for(i <- 0 until length if flag) yield{
var item = 0
if(i+1 >= length){
flag = false
}
if(i%2 == 0){
item = array(i+1)
}else if(i%2 == 1){
item = array(i-1)
}
item
}
newArray.toArray
}

3.4
给定一个整数数组,产生一个新的数组,包含元数组中的所有正值,以原有顺序排列,之后的元素是所有零或负值,以原有顺序排列

答:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def sortArray(array:Array[Int]):Array[Int] = {
val newArrayBuffer = new ArrayBuffer[Int]()
val positiveIndex = for(i <- 0 to array.length if array(i) > 0 ) yield {array(i)}
newArrayBuffer ++= positiveIndex.toBuffer
newArrayBuffer ++= (for(i <- 0 to array.length if !positiveIndex.contains(i)) yield{array(i)}).toBuffer
newArrayBuffer.toArray
}
def sortArray2(array:Array[Int]):Array[Int] = {
val a = ArrayBuffer[Int]()
val b = ArrayBuffer[Int]()
array.foreach(arg => if(arg > 0) a += arg else b += arg)
a ++= b
a.toArray
}
def sortArray3(array:Array[Int]):Array[Int] = {
val a = array.filter(_>0).map(_*1)
val b = array.filter(_<=0).map(_*1)
var c = a.toBuffer
c ++= b.toBuffer
c.toArray
}

3.5
如何计算Array[Double]的平均值

答:

1
2
3
def avgArray(array:Array[Double]):Double={
array.sum/array.length
}

3.6
如何重新组织Array[Int]的元素将他们以反序排列?对于ArrayBuffer[Int]你又会怎么做呢?

答:

1
2
3
def reverseArray(arr:Array[Int]):Array[Int]={
arr.reverse
}

3.7
编写一段代码,产出数组中的所有值,去掉重复项。(提示:查看Scaladoc)

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
def removeArray1(arr:Array[Int]):Array[Int]={
val t = arr.toBuffer
val idx =(for(i <- 0 until t.length if t(i) < 0) yield { i }).reverse.toBuffer
idx.trimEnd(1)
idx.foreach(arg => t.remove(arg))
t.toArray
}
def removeArray2(array:Array[Int]):Array[Int]={
val arr = array.toBuffer
var first = true
val indexes = for(i <- 0 until arr.length if first || arr(i) >= 0) yield{
if(arr(i) < 0) first = false;i
}
for(j <- 0 until indexes.length) arr(j) = arr(indexes(j))
arr.trimEnd(arr.length - indexes.length)
arr.toArray
}
def performance():Unit={
val array = Array(1,-2,3,55,3,-4,-6,-2,-1,55,-3,5)
val startTime1 = System.currentTimeMillis()
for(i <- 0 to 10000) {
removeArray1(array)
}
val endTime1 = System.currentTimeMillis()
val startTime2 = System.currentTimeMillis()
for(i <- 0 to 10000) {
val result2 = removeArray2(array)
}
val endTime2 = System.currentTimeMillis()
println("removeArray1 cost:"+(endTime1-startTime1))
println("removeArray2 cost:"+(endTime2-startTime2))
//10000次 201:81
}
/**
* scala> performance
removeArray1 cost:231
removeArray2 cost:163
scala> performance
removeArray1 cost:16
removeArray2 cost:14
scala> performance
removeArray1 cost:12
removeArray2 cost:6
scala> performance
removeArray1 cost:13
removeArray2 cost:7
scala> performance
removeArray1 cost:18
removeArray2 cost:5
*/

3.9
创建一个由java.util.TimeZone.getAvailableIDs返回ide时区集合,判断条件是它们在美洲。去掉”America/“前缀并排序

答:

1
2
3
4
5
def sortAmericaTimeZone():Unit = {
val array = java.util.TimeZone.getAvailableIDs
val result = array.filter(_.startsWith("America")).map(_.replace("America/","")).sorted
result.foreach(println(_))
}

3.10
引入java.awt.datatransfer.并构建一个类型为SystemFlavorMap类型的对象:
val flavors = SystemFlavorMap.getDefaultFlavorMap().asInstanceOf[SystemFlavorMap]
然后以DataFlavor.imageFlavor为参数调用getNativesForFlavor方法,以Scala缓冲保存返回值。
(为什么用这样一个晦涩难懂的类?因为在Java标准库中很难找到使用java.util.List的代码)

答:

1
2
3
4
5
6
7
8
9
import java.awt.datatransfer.{DataFlavor, SystemFlavorMap}
import collection.JavaConversions.asScalaBuffer
import scala.collection.mutable
def test():Unit = {
val flavors = SystemFlavorMap.getDefaultFlavorMap().asInstanceOf[SystemFlavorMap]
val res:mutable.Buffer[String] = flavors.getNativesForFlavor(DataFlavor.imageFlavor)
println(res)
}

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

第二章 控制结构与函数

2.1
一个数字如果为正数,则它的signum为1;如果是负数,则signum为-1;如果为0,则signum为0.编写一个函数来计算这个值。

答:

1
2
3
4
5
6
7
def sigNum(num: Int):Int = {
if (num > 0) 1 else if (num < 0) -1 else 0
}
println(signum(10000));
println(signum(-22));
println(signum(0));

2.2
一个空的块表达式{}的值是什么?类型是什么?

答:

1
2
scala> val x = {}
x: Unit = ()

2.3
指出在Scala中何种情况下赋值语句x=y=1是合法的。(提示:给x找个合适的类型定义)

答:
因为赋值语句的值是Unit型,所以x可定义为Unit型。

1
2
3
4
5
6
7
8
scala> var y = 5
y: Int = 5
scala> var x = {}
x: Unit = ()
scala> x = y= 1
x: Unit = ()

2.4
针对下列Java循环编写一个Scala版本:
for(int i=10;i>=0;i—) System.out.println(i);

答:

1
2
3
4
5
6
7
8
9
10
11
12
13
scala> for(i <- 0 to 10 reverse) println(i)
warning: there was one feature warning; re-run with -feature for details
10
9
8
7
6
5
4
3
2
1
0

2.5
编写一个过程countdown(n:Int),打印从n到0的数字。

答:

1
2
3
4
5
6
7
8
9
10
11
12
scala> def countdown(n:Int){
for(i <- 0 to n reverse){
println(i)
}
}
warning: there was one feature warning; re-run with -feature for details
countdown: (n: Int)Unit
scala> countdown(2)
2
1
0

2.6
编写一个for循环,计算字符串中所有字母的Unicode代码的乘积。举例来说,”Hello”中所有字符串的乘积为9415087488L

答:

1
2
3
4
5
6
7
8
9
scala> def test(str:String):Long = {
var result:Long = 1
for(i <- str) result *= i
result
}
test: (str: String)Long
scala> test("oh,yeah")
res5: Long = 62621117315328

2.7
同样是解决前一个练习的问题,但这次不使用循环。(提示:在Scaladoc中查看StringOps)

答:

1
2
3
4
5
def test(str:String):Long = {
var result:Long = 1
str.foreach( ch => {result *= (ch.toLong)})
result
}

2.8
编写一个函数product(s:String), 计算前面习题中提到的乘积。

答:

1
2
3
4
5
def product(str:String):Long = {
var result:Long = 1
str.foreach( result *= _.toLong )
result
}

2.9
把前一个练习中的函数改成递归函数。

答:

1
2
3
4
5
6
7
8
def product(str:String):Long = {
var result:Long = 1
if (str.length==1)
result = str.head.toLong
else
result = str.head.toLong * product(str.tail)
result
}

2.10
编写函数计算x^n, 其中n为整数。使用如下的递归定义:

  • x^n = y^2,如果
  • x^n = x*x^(n-1)
  • x^0 = 1
  • x^n = 1/x^(-n)

答:

1
2
3
4
5
6
def getPower(x:Double, n:Int):Double = {
if (n == 0 ) 1
else if ( n>0 && n % 2 != 0 ) x * getPower(x, n-1)
else if (n >0 && n % 2 == 0 ) getPower (x, n/2) * getPower (x, n/2)
else 1/getPower( x, -n)
}

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

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