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
}