1. 程式人生 > >快學Scala習題解答—第十一章 操作符

快學Scala習題解答—第十一章 操作符

+= reac gin 運行 eric 兩種 實例 大小 span

11 操作符
11.1 依據優先級規則,3 + 4 -> 5和3 -> 4 + 5是怎樣被求值的?
在REPL中運行就可以得到結果。都是從左至右運行
12.2 BigInt類有一個pow方法,但沒實用操作符字符。

Scala類庫的設計者為什麽沒有選用**(像Fortran那樣)或者^(像Pascal那樣)作為乘方操作符呢?
Scala中的操作符就是方法。其優先級是依據首字母來推斷的,優先級例如以下
最高優先級:除下面字符外的操作符字符
* / %
+ -
:
= !
< >
&
?
|
非操作符
最低優先級:賦值操作符
一般乘方的操作符是優於乘法操作的。假設使用**作為乘方的話,那麽其優先級則與*同樣。而假設使用^的話,則優先級低於*操作。優先級都是有問題的。故沒有使用這兩種操作符
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)
...
}

import 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={
    newFrac((this.num * other.den) + (other.num * this.den),this.den * other.den)
  }

  def -(other:Fraction):Fraction={
    newFrac((this.num * other.den) - (other.num * this.den),this.den * other.den)
  }

  def *(other:Fraction):Fraction={
    newFrac(this.num * other.num,this.den * other.den)
  }

  def /(other:Fraction):Fraction={
    newFrac(this.num * other.den,this.den * other.num)
  }

  private def newFrac(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。

你應該同一時候提供*和/操作符嗎?為什麽?

class Money(val dollar:BigInt,val cent:BigInt){

  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)
  }

  private def toCent()={
    this.dollar * 100 + this.cent
  }

  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
}

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"應產出:<table><tr><td>Java</td></tr><td>Scala</td></tr><tr><td>Gosling…
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 /
(__|__) -----

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

class BigSequence{
  var num = new Array[Int](64)

  for (i <- 0 until num.length){
    num(i) = -1
  }

  def pack():Long={
    num.filter(_ >= 0).mkString.toLong
  }
}

object BigSequence{

  def apply(num:Int):BigSequence={
    val b = new BigSequence
    var i = 0
    num.toString.foreach{
      n=>
      b.num(i) = n.getNumericValue
      i+=1
    }
    b
  }

  def main(args: Array[String]) {
    val b = BigSequence(10100)
    println(b.pack())
  }
}


11.8 提供一個Matrix類—你能夠選擇須要的是一個2*2的矩陣。隨意大小的正方形矩陣。或m*n的矩陣。

支持+和*操作。*操作應相同適用於單值,比如mat*2。單個元素能夠通過mat(row,col)得到

class Matrix(val x:Int,val y:Int){

  def +(other:Matrix):Matrix={
    Matrix(this.x + other.x,this.y + other.y)
  }

  def +(other:Int):Matrix={
    Matrix(this.x + other,this.y + other)
  }

  def *(other:Matrix):Matrix={
    Matrix(this.x * other.x,this.y * other.y)
  }

  def *(other:Int):Matrix={
    Matrix(this.x * other,this.y * other)
  }

  override def toString()={
    var str = ""
    for(i <- 1 to x){
      for(j <- 1 to y){
        str += "*"
      }
      str += "\n"
    }
    str
  }
}

object Matrix{
  def apply(x:Int,y:Int):Matrix= new Matrix(x,y)

  def main(args: Array[String]) {
    val m = Matrix(2,2)
    val n = Matrix(3,4)
    println(m)
    println(n)
    println(m + n)
    println()
    println(m * n)
    println()
    println(m + 2)
    println()
    println(n * 2)
    println()
  }
}

11.9 為RichFile類定義unapply操作,提取文件路徑,名稱和擴展名。舉例來說,文件/home/cay/readme.txt的路徑為/home/cay,名稱為readme,擴展名為txt
class RichFile(val path:String){}
object RichFile{
  def apply(path:String):RichFile={
    new RichFile(path)
  }

  def unapply(richFile:RichFile) = {
    if(richFile.path == null){
      None
    } else {
      val reg = "([/\\w+]+)/(\\w+)\\.(\\w+)".r
      val reg(r1,r2,r3) = richFile.path
      Some((r1,r2,r3))
    }
  }

  def main(args: Array[String]) {
    val richFile = RichFile("/home/cay/readme.txt")
    val RichFile(r1,r2,r3) = richFile
    println(r1)
    println(r2)
    println(r3)
  }
}

11.10 為RichFile類定義一個unapplySeq。提取全部路徑段。

舉例來說,對於/home/cay/readme.txt。你應該產出三個路徑段的序列:home,cay和readme.txt

class RichFile(val path:String){}

object RichFile{
  def apply(path:String):RichFile={
    new RichFile(path)
  }

  def unapplySeq(richFile:RichFile):Option[Seq[String]]={
    if(richFile.path == null){
      None
    } else {
      Some(richFile.path.split("/"))
    }
  }

  def main(args: Array[String]) {
    val richFile = RichFile("/home/cay/readme.txt")
    val RichFile(r @ _*) = richFile
    println(r)
  }
}


快學Scala習題解答—第十一章 操作符