1. 程式人生 > >scala小練習四

scala小練習四

1.一千萬個隨機數,隨機數範圍在1到1億之間,現在要求寫出一種演算法,將1到1億之間沒有出現的隨機數求出來
第一題看這裡

2 編 寫 一 個 函 數 , 接 收 一 個 字 符 串 集 合 , 以 及 一 個 從 字 符 串 到 整 數 的 映 射
,返回整數集合,其值為能和集合中某個字串相應的對映值。舉例來說,給Array(“Tom”,”Fred”,”Harry”) 和Map(“Tom”->3,”Dick”->4,”Harry”->5),返回Array(3,5)

做了兩種實現,一種是map,一種是flatMap(註釋部分)

//map將集合中的每個元素處理,並將處理後的結果返回,返回的是option
// 而flatMap與map唯一不一樣的地方就是傳入的函式在處理完後返回值必須是List def func(a: Array[String], m: Map[String, Int]): Array[Int] = { //val res = a.flatMap(m.get(_))//簡單的實現 var ans: Array[Int] = Array[Int]() val r = a.map(m.get(_)) for (a <- r) { ans = show(a, ans) } //從Option中取出Some中資料 def show(r: Option[Int], arr: Array
[Int]) = r match { case None => arr case Some(s) => arr :+ s } ans }

這裡寫圖片描述

3.對給定的整型列表lst,(lst :\ ListInt)(:: )得到的是什麼? (ListInt /: lst)(:+)又得到什麼?如何修改這兩個操作,對原來列表反向操作?

val a=(lst :\ ListInt)(::)
等價於
val c=lst.foldRight(ListInt)(+:)
相當於foldRight,新增lst中的元素到ListInt,初始是一個空List
從右開始操作,每次新增的元素在左側,所以只能用::或+:

val b=(ListInt/:lst)(:+)
等價於
val d=lst.foldLeft(ListInt)(:+)
foldLeft,從左向右新增元素到列表,所以只能用:+

val lst =List[Int](1,2,3,4,5)
    val res1=(lst :\ List[Int]())((x,y)=>y:+x)//正向是x+:y,反過來即可
    println("反向:"+res1)
    val res2=(List[Int]() /: lst)((x,y)=>y+:x)//正向是x:+y,反過來即可
    println("反向:"+res2)

這裡寫圖片描述

4.根據 flatMap 實現一個variance(方差)函式,如果一個序列的平均值m,variance是對序列中的每一個元素x進行math.pow(x-m,2) def variances(xs: Seq[Double]): Option[Double]

def variances(xs: Seq[Double]): Option[Double] = {
    val l=xs.length
    val m = xs.sum / l
    Some(xs.map(x=>math.pow(x-m,2)).sum/l)
  }

這裡寫圖片描述

5.統計字串中字母出現的頻率對映,例如字串 “scalajavajavascript”返回Map[Char,Int]。用aggregate 方法

val a="scalajavajavascript".par.aggregate(HashMap[Char,Int]())(
      (k,v)=>{//匿名函式,k列印得kMap(),是要返回的HashMap,v是單個字母
        k+(v->(k.getOrElse(v,0)+1))//返回一個HashMap[Char,Int]
      }
      ,
      //當不使用並行的時候,只有上部分就已經可以,這部分直接返回就可以
      (k,v)=>(k.keySet++v.keySet).foldLeft((HashMap[Char,Int]())) {//k列印得kMap(),v列印得vMap()\
        (res,keys)=>res+(keys->(k.getOrElse(keys,0)+v.getOrElse(keys,0)))
      }
    )

這裡寫圖片描述

6.寫出 ImageReader 的實現類

object work06 {
  trait Reader[T] {
    //read方法返回值是泛型,通過實現類的時候指定
    def read(fileName: String): T
  }

  class StringReader extends Reader[String]{
    def read(fileName: String) = Source.fromFile(fileName, "UTF-8").mkString
  }

  class ImageReader extends Reader[BufferedImage]{
    override def read(fileName: String): BufferedImage = ImageIO.read(new File(fileName))
  }
}
def main(args: Array[String]): Unit = {
    val a = new ImageReader
    println(a.read("image.png"))
  }

拿一張圖片簡單測試一下
這裡寫圖片描述

真的讀到了圖片

這裡寫圖片描述

7.定義一個不可變類Pair[T,S],帶一個swap 方法,返回陣列交換過位置的新對偶。

class Fair[T,S](a:(T,S)){
  def swap:(S,T)={
    (a._2,a._1)
  }
}
object work07 {
  def main(args: Array[String]): Unit = {
    val a=new Pair(1,'a')
    val b=a.swap
    println(b)
  }
}

這裡寫圖片描述

8 如果我們想把Pair[Person]的第一個元件替換為Student,為什麼不需要給 replaceFirst 方法定義一個下界(程式碼不是答案,答案是文字)

object VarianceDemo {
  def makFriends(p: Pair[Person]): Unit = {
  }
  def main(args: Array[String]) {
     val p = new Pair[Student](new Student(), new Student())
     //VarianceDemo.makFriends(p)
  }
}
class Pair[T](val first: T, val second: T) {
  def replaceFirst(newFirst: T) = new Pair[T](newFirst, second)
}
class Person {}
class Student extends Person {}

答案:
因為Student是Person的子型別,可以直接去替換Person,根本不需要定義下界
那什麼時候用下界呢?
下界 R>: T 定義泛型R,R是T的超類
所以下界替換進來的類常常是原型別的超型別
如果我們需要用Person替換Student(超類替換子類),我們需要定義下界,程式碼如下

object VarianceDemo {
  def main(args: Array[String]) {
     val p = new Pair[Student](new Student(), new Student())
    val a=new Person
    p.replaceFirst(a)
  }
}
class Pair[T](val first: T, val second: T) {
  def replaceFirst[R>:T](newFirst: R) = new Pair[R](newFirst, second)
}
class Person {}
class Student extends Person {}

如果不定義下界,也可以通過編譯,要求返回的 new Pair後面不指定[T],此時返回的型別是Any
由此,可以理解‘界’這個概念

class Pair[T](val first: T, val second: T) {
  def replaceFirst[T](newFirst: T) = new Pair(newFirst, second)
}

當然有種特別的情況
上述程式碼,R會被什麼具體型別替換,取決於T和 newFirst 的型別。如果 newFirst 的型別剛好是T的基類,,R就直接是 newFirst 的型別。如果 newFirst 的型別不是T的基類,那R就會是T和 newFirst 的型別的共同基類

這道題真是給我整蒙了,甚至讓我懷疑人生,因為剛剛看完協變,第一眼就把下界看成協變了,雖然後來看到是人家問的是下界,但協變就一直沒從我腦中驅除,思維一直在協變和下界中穿梭,絞盡腦汁想著哪用上協變了啊,其實人家根本沒問好嘛,這個過程雖煎熬,但也確實讓我把這型變和界理解得更透徹

9 檢視 Iterable [+A] 特質。哪些方法使用了型別引數A?為什麼在這些方法中型別引數是協變的

foldLeft, foldRight等方法,因為Iterable特質宣告型別A為協變

10 定義一個操作符+%,將一個給定的百分比新增到某個值。舉例來說120 +% 10 = 132,由於操作符是方法而不是函式,你需要提供一個implicit

object work10 {
  //隱式轉換的宣告必須在要使用的位置之前,否則無效
  //因為Double型別中並沒有+%這個方法,所以這裡把Double型別轉換為A型別
  implicit def doubleToA(x:Double)=new A(x)
  def main(args: Array[String]): Unit = {
    val res=120 +% 10
    println(res)
  }
}
class A(x:Double){
  def +%(y:Double)=x*y*0.01+x
}

這裡寫圖片描述