1. 程式人生 > >Scala練習(十一)

Scala練習(十一)

\1. 根據優先順序規則, 3 + 4 -> 5 和 3 -> 4 + 5 是如何被求值的?

1
在REPL中執行即可得到結果。都是從左至右執行

\2. BigInt 類有一個pow方法,但沒有用操作符字元,Scala類庫的設計者為什麼沒有選用**(像Fortran那樣)或者^(像Pascal那樣)作為乘方操作符呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Scala中的操作符就是方法,其優先順序是根據首字母來判斷的,優先順序如下

最高優先順序:除以下字元外的操作符字元
 * / % 
 + - 
 : 
 = ! 
 < > 
 & 
 ˆ 
 | 
 非操作符
 最低優先順序:賦值操作符

 一般乘方的操作符是優於乘法操作的,如果使用**作為乘方的話,那麼其優先順序則與*相同,而如果使用^的話,則優先順序低於*操作。優先順序都是有問題的。故沒有使用這兩種操作符

\3. 實現Fraction類,支援 + - * / 操作,支援約分, 例如將 15 / -6 變成 -5 / 2。除以最大公約數,像這樣:

1
2
3
4
5
6
7
8
9
10
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) a.abs 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
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) a.abs else gcd(b, a % b) def +(a: Fraction) : Fraction = { return new Fraction(this.num * a.den + a.num * this.den, this.den * a.den); } def -(a: Fraction) : Fraction = { return new Fraction(this.num * a.den - a.num * this.den, this.den * a.den); } def *(a: Fraction) : Fraction = { return new Fraction(this.num * a.num , this.den * a.den); } def /(a: Fraction) : Fraction = { return new Fraction(this.num * a.den , this.den * a.num); } } object FractionTest extends App { val num1 = new Fraction(3, 4); val num2 = new Fraction(1, 2); println(num1); println(num2); println(num1 + num2); println(num1 - num2); println(num1 * num2); println(num1 / num2); }

\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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
class Money(private val d: Int, private val c: Int) {
  private var dollars = d;

  private val cents = {
    if (c >= 100) {
      dollars += c / 100
      c % 100
    } else if (c < -100) {
      dollars -= c / 100
      c % 100
    } else {
      c
    }
  } 
  
  override def toString = {
    "(" + dollars + "," + cents + ")"
  } 
  
  def +(a: Money) = {
    new Money(this.dollars + a.dollars, this.cents + a.cents);
  } 
  
  def -(a: Money) = {
    new Money(this.dollars - a.dollars, this.cents - a.cents);
  } 
  
  def ==(a: Money) = {
    (this.dollars * 100 + this.cents) == (a.dollars * 100 + a.cents)
  } 
  
  def <(a: Money) = {
    (this.dollars * 100 + this.cents) < (a.dollars * 100 + a.cents)
  } 
}

object Money {
  def apply(d: Int, c: Int) = new Money(d, c);
  
  def unapply(o: Money) = {
    Some((o.dollars, o.cents))
  } 
} 

object MoneyTest extends App {
  val c = Money(2, 175);
  val Money(a: Int, b: Int) = c

  println(a)
  println(Money(1, 75) + Money(0, 50) == Money(2, 25))
}

對於美元操作乘、除是沒有意義.

\5. 提供操作符用於構造HTML表格。例如:

1
Table() | "Java" | "Scala" || "Gosling" | "Odersky" || "JVM" | "JVM, .NET"

應產出:

1
<table><tr><td>Java</td><td>Scala</td></tr><tr><td>Gosling</td><td>Odersky</td></tr><tr><td>JVM</td><td>JVM, .NET</td></tr></table>
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
import scala.collection.mutable.ArrayBuffer

class Table
{
  private val trTags : ArrayBuffer[String] = ArrayBuffer();
  private val tdTags : ArrayBuffer[String] = ArrayBuffer();

  def |(str: String) = {
    tdTags += str
    this
  }

  def ||(str: String) : Table= {
    trTags += "<tr><td>" + tdTags.mkString("</td><td>") + "</td><tr>"
    tdTags.clear()     
    tdTags += str
    this
  }

  override def toString = {
    if (!tdTags.isEmpty) {
      trTags += "<tr><td>" + tdTags.mkString("</td><td>") + "</td><tr>"
    } 
    
    "<table>" + trTags.mkString("")+ "</table>"
  } 
} 

object Table {
  def apply() = new Table()
} 

object TableTest extends App {
  val str = Table() | "Java" | "Scala" || "Gosling" | "Odersky" || "JVM" | "JVM, .NET"
  println(str)
}

\6. 提供一個ASCIIArt 類,其物件包含類似這樣的圖形:

1
2
3
4
5
 /\_/\
( ' ' )
(  _  )
 | | |
(__|__)

提供將兩個ASCIIArt圖形橫向或縱向結合的操作符,選用適當優先順序的操作符命名。橫向結合的例項:

1
2
3
4
5
 /\_/\    -----
( ' ' ) / 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
import scala.collection.mutable.ArrayBuffer

class ASCIIArt(str: String) {
  val artMap : ArrayBuffer[String] = new ArrayBuffer[String]()
  if (str != null && !str.trim.eq("")) {
    str.split("[\r\n]+").foreach((line: String) => {
      val tmp = new ArrayBuffer[String]();
      artMap += line
    })
  }

  def this() {
    this("")
  }

  def +(other: ASCIIArt) : ASCIIArt = {
    val art = new ASCIIArt()
    val length = if (this.artMap.length >= other.artMap.length) this.artMap.length else other.artMap.length
    for (i <- 0 until length) {
      val thisCur : String = if (this.artMap.isDefinedAt(i)) this.artMap(i) else "" 
      val otherCur : String = if (this.artMap.isDefinedAt(i)) other.artMap(i) else ""  
      art.artMap += thisCur + otherCur
    } 

    art
  }

  def *(other: ASCIIArt) : ASCIIArt = {
    val art = new ASCIIArt()
    art.artMap ++= this.artMap
    art.artMap ++= other.artMap
    art
  }    
       
  override def toString = {
    artMap.mkString("\n") 
  }    
} 

object ASCIIArt {
  def apply(str: String) = new ASCIIArt(str)
} 

object ASCIIArtTest extends App {
  val a1 = ASCIIArt(""" /\_/\
                      |( ' ' )
                      |(  -  )
                      | | | | 
                      |(__|__)
                      |""".stripMargin)
  val a2 = ASCIIArt("""    -----
                      |  / Hello \
                      | <  Scala |
                      |  \ Coder /
                      |    -----
                      |""".stripMargin)
  println(a1 + a2)
  println(a1 * a2)
}

\7. 實現一個BigSequence 類,將64個bit的序列包在一個Long值中。提供apply和update操作來獲取和設定某個位置具體的bit

1
// todo

\8. 提供一個Matrix類—-你可以選擇需要的是一個2x2的矩陣,任意大小的正方形矩陣,或是mxn的矩陣。支援+和*操作。*操作應同樣適用於單值,例如 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
61
62
63
64
65
66
67
import scala.collection.mutable.ArrayBuffer
class Matrix(private val data: Array[Int], private val nrow: Int){
  private val matrixData : Array[Array[Int]] = {
    val cols = (data.length.toFloat / nrow).ceil.toInt
    val result : Array[Array[Int]] = Array.ofDim[Int](nrow, cols);
    for (i <- 0 until nrow) {
      for (j <- 0 until cols) {
        val index = i*cols + j
        result(i)(j) = if (data.isDefinedAt(index)) data(index) else 0
      }
    }
    result
  }

  override def toString = {
    var str = ""
    matrixData.map((p: Array[Int]) => {
      p.mkString(",")
    }).mkString("\n")
  }

  def *(a: Matrix) = {
    val data: ArrayBuffer[Int] = ArrayBuffer();
    for (i <- 0 to  a.matrixData.length - 1) {
      for (j <- 0 to a.matrixData(0).length - 1) {
        data += a.matrixData(i)(j) * this.matrixData(i)(j)
      }
    }

    new Matrix(data.toArray, a.matrixData.length)
  }

  def *(a: Int) = {
    val data: ArrayBuffer[Int] = ArrayBuffer();
    for (i <- 0 to  this.matrixData.length - 1) {
      for (j <- 0 to this.matrixData(0).length - 1) {
        data += this.matrixData(i)(j) * a 
      }
    }

    new Matrix(data.toArray, this.matrixData.length)
  }

  def +(a: Matrix) = {
    val data: ArrayBuffer[Int] = ArrayBuffer();
    for (i <- 0 to  this.matrixData.length - 1) {
      for (j <- 0 to this.matrixData(0).length - 1) {
        data += this.matrixData(i)(j) + a.matrixData(i)(j)
      }
    }

    new Matrix(data.toArray, this.matrixData.length)
  }

  def mat(row: Int, col: Int) = {
    matrixData(row - 1)(col - 1)
  }
}

object MatrixTest extends App {
  val m = new Matrix(Array(1,2,3,4), 3)
  val n = new Matrix(Array(1,2,3,4), 3)
  println(m * n)
  println(m + n)
  println(m.mat(2, 2))
  println(n * 10)
}

\9. 為RichFile 類定義unapply操作,提取檔案路勁、名稱和副檔名。舉例來說,檔案/home/cay/readme.txt的路勁為/home/cay, 名稱為 readme, 副檔名txt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.io.File
object RichFile { 

  def unapply(filePath : String) = {
    val file = new File(filePath)
    val ext = file.getName.split("\\.")
    Some((file.getParent, file.getName, ext(1)))
  }                                    
}                                      

object RichFileTest extends App {
  val RichFile(path, fileName, ext) = "/home/cay/readme.txt"
  println(path)
  println(fileName)
  println(ext)
}

\10. 為RichFile 類定義一個unapplySeq, 提取所有路階段。舉例來說,對於/home/cay/readme.txt,你應該產出三個路勁的序列: home,cay,readme.txt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
object RichFile {
  def unapplySeq(filePath : String) : Option[Seq[String]]= {
    Some(filePath.trim.split("\\/"))
  } 
} 

object RichFileTest extends App {
  val str = "/home/cay/readme.txt"
  str match {
    case RichFile(str0, str1, str2, str3) => {
      println(str1);
      println(str2);
    } 
    case _ => {println(str)}
  } 
}