《快学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”应产出:
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 ,请勿用于任何商业用途)