Scala知識點彙總

Scala數組小結

1.定長數組

定長數組:指長度不可變的數組Array。
第一種方式:
先聲明一個數組,後初始化該數組:javascript

scala> val array = new Array[Double](5)
array: Array[Double] = Array(0.0, 0.0, 0.0, 0.0, 0.0)

賦值方式:array(index) = value
第二種方式:php

scala> val array = Array(1, 2, 3, 4, 5)
array: Array[Int] = Array(1, 2, 3, 4, 5)

第三種方式,Array.fill(length)(value):html

scala> val array = Array.fill(5)(3.5)
array: Array[Double] = Array(3.5, 3.5, 3.5, 3.5, 3.5)

若是fill第二個參數只寫一個值的話,那麼該數組的全部元素都是該值,可是若是第二個參數是一個iterator或者random,那麼數組就會被賦值爲它們的值。java

scala> val array = Array.fill(2)(math.random)
array: Array[Double] = Array(0.2810736748034083, 0.7261142068882558)

第四種方式,ofDim[T](length):python

scala> val array = Array.ofDim[Double](5)
array: Array[Double] = Array(0.0, 0.0, 0.0, 0.0, 0.0)

賦值方式:array(index) = valuenginx

2.變長數組

變長數組:指數組長度能夠變化。
聲明一個空的數組,此時數組沒有任何元素,且不能如下標索引賦值:數組

val array = new ArrayBuffer[T]()

在數組尾部增長一個類型爲T的元素e1:ruby

array += e1

在數組尾部增長一個類型爲T的元素e一、e2:markdown

array += (e1, e2)

在數組尾部增長一個類型爲T的數組:閉包

array ++= Array(e1, e2)

在第index元素後插入e1:

array.insert(index, e1)

在第index元素後插入e一、e2:

array.insert(index, e1, e2)

移除第index元素後的n個元素:

array.remove(index, n)

3多維數組

3.1定長多維數組

第一種方式:

val array = new Array[Array[Int]](5) 
scala> val array = new Array[Array[Int]](5)
array: Array[Array[Int]] = Array(null, null, null, null, null)

此時,內部的每一個數組長度是不同的,相似於Java中的以下定義:

int[][] array = new int[2][];

第二種方式(推薦):
用ofDim[T](rows, column, height,...)函數定義,但最多能夠定義五維數組,不過已經夠用了。

scala> val array = Array.ofDim[Int](2,3)
array: Array[Array[Int]] = Array(Array(0, 0, 0), Array(0, 0, 0))

 val dimArray = Array.tabulate(3, 4)((a, b) => (a + 1, b + 2))
    dimArray.foreach(println)
    for (i <- 0 until 3;j<-0 until 4)
      println(dimArray(i)(j))

 

3.2變長多維數組

scala> val arr1 = new ArrayBuffer[ArrayBuffer[Int]]()
arr1: scala.collection.mutable.ArrayBuffer[scala.collection.mutable.ArrayBuffer[Int]] = ArrayBuffer()

4.定長數組和變長數組的轉換

定長數組imarray轉換成變長數組array:

array = imarray.toBuffer

變長數組array轉換成定長數組imarray:

imarray = array.toArray

當定/變長多維數組相互轉換時,只轉換最外面那層數組:

scala> val array = Array(Array(1,2),Array(3,4))
arr: Array[Array[Int]] = Array(Array(1, 2), Array(3, 4))

scala> array.toBuffer
res14: scala.collection.mutable.Buffer[Array[Int]] = ArrayBuffer(Array(1, 2), Array(3, 4))


scala for yield 的用法

 Scala中的yield的主要做用是記住每次迭代中的有關值,並逐一存入到一個數組中。
用法以下:
for {子句} yield {變量或表達式}*/
case class User(name:String,age:Int,sex:String)
object test{
def foo(n:Int,v:Int)={
for(i<-0 until n;
j<- i until n if i+j==v)
print(s"($i, $j)")

}
def main(args: Array[String]): Unit = {
val users=List(new User("gmy",25,"male"),new User("ggh",22,"female"),new User("wss",25,"female"),new User("zan",23,"female"),new User("wy",23,"female"))
val yeildUser=for(user<-users if user.sex.equals("male")||user.age<23)
yield user.name //生成列表
yeildUser.foreach(user=>println(user)) //gmy ggh
println(foo(10,10)) //(1, 9)(2, 8)(3, 7)(4, 6)(5, 5)()
}
}

 

 1 case class User(name:String,age:Int,sex:String)
 2 object YielldFor {
 3     def foo(n:Int,v:Int)={
 4       for(i<-0 until n;
 5           j<- i until n if i+j==v)
 6         print(s"($i, $j)")
 7 
 8     }
 9 
10   def foo1(n:Int,v:Int)={
11     for(i<-0 until n;
12         j<- i until v )
13       print(s"($i, $j)")
14 
15   }
16 
17   def foo2(n:Int,v:Int)={
18     for(i<-0 to n;
19         j<- i to v )
20       print(s"($i, $j)")
21 
22   }
23     def main(args: Array[String]): Unit = {
24       val users=List(new User("gmy",25,"male"),new User("ggh",22,"female"),new User("wss",25,"female"),new User("zan",23,"female"),new User("wy",23,"female"))
25       val yeildUser=for(user<-users if user.sex.equals("male")||user.age<23)
26         yield user.name   //生成列表
27       yeildUser.foreach(user=>println(user))  //gmy   ggh
28       println(foo(10,10))   //(1, 9)(2, 8)(3, 7)(4, 6)(5, 5)()
29       println(foo1(2,5))
30       println(foo2(10,5))
31     }
32 
33 }

scala中的apply方法與unapply方法

1.apply方法

當scala中類或者對象有一個主要用途的時候,apply方法就是一個很好地語法糖。

請看下面一個簡單的例子:

class Foo(foo: String) { } object Foo { def apply(foo: String) : Foo = { new Foo(foo) } }

 

定義了一個Foo類,而且在這個類中,有一個伴生對象Foo,裏面定義了apply方法。有了這個apply方法之後,咱們在調用這個Foo類的時候,用函數的方式來調用:

object Client {

    def main(args: Array[String]): Unit = { val foo = Foo("Hello") } }

 

咱們用Foo("Hello")的方式,就獲得了一個Foo類型的對象,這一切就是apply方法的功勞。若是沒有apply方法,咱們將須要使用new關鍵字來獲得Foo對象。

2.apply方法用來作工廠

apply方法的最佳實踐方式之一就是用來作工廠。好比在Scala的標準庫中,許多集合類給咱們提供了apply方法來建立集合:

object Client {

    def main(args: Array[String]): Unit = { val arr = new Array[Int](3) arr(0) = 0 arr(1) = 1 arr(2) = 2 arr.foreach(x => print(x + " ")) println() val array = Array(1,2,3) array.foreach(x => print(x + " ")) } }

 

上面兩種方式咱們均可以用來建立Array。第一種方式是使用new關鍵字,這是傳統的面向對象的方式。那麼第二種方式是什麼狀況呢?若是咱們在IDE裏點進去,能夠發現IDE會提示咱們有一個apply方法。點進去看apply方法的源碼:

/** Creates an array of `Int` objects */ // Subject to a compiler optimization in Cleanup, see above. def apply(x: Int, xs: Int*): Array[Int] = { val array = new Array[Int](xs.length + 1) array(0) = x var i = 1 for (x <- xs.iterator) { array(i) = x; i += 1 } array }

 

 

3.unapply方法

從上面的例子不難看出,apply方法有點相似於java中的構造函數,接受構造參數變成一個對象。那麼unapply方法就恰好相反,他是接受一個對象,從對象中提取出相應的值。 
unapply方法主要用於模式匹配中。 
看個簡單的例子:

class Money(val value: Double, val country: String) {} object Money { def apply(value: Double, country: String) : Money = new Money(value, country) def unapply(money: Money): Option[(Double, String)] = { if(money == null) { None } else { Some(money.value, money.country) } } }

客戶端實現:

def testUnapply() = {
        val money = Money(10.1, "RMB") money match { case Money(num, "RMB") => println("RMB: " + num) case _ => println("Not RMB!") } }

 

 

最後輸出爲:

RMB: 10.1

Scala中閉包

在Scala中,函數引入傳入的參數是再正常不過的事情了,好比
(x: Int) => x > 0中,惟一在函數體x > 0中用到的變量是x,即這個函數的惟一參數。

除此以外,Scala還支持引用其餘地方定義的變量:
(x: Int) => x + more,這個函數將more也做爲入參,不過這個參數是哪裏來的?從這個函數的角度來看,more是一個自由變量,由於函數字面量自己並無給more賦予任何含義。相反,x是一個綁定變量,由於它在該函數的上下文裏有明確的定義:它被定義爲該函數的惟一參數。若是單獨使用這個函數字面量,而沒有在任何處於做用域內的地方定義more,編譯器將報錯:

scala> (x: Int) => x + more <console>:12: error: not found: value more (x: Int) => x + more 

另外一方面,只要能找到名爲more的變量,一樣的函數字面量就能正常工做:

scala> var more = 1 more: Int = 1 scala> val addMore = (x: Int) => x + more addMore: Int => Int = $$Lambda$1104/583744857@33e4b9c4 scala> addMore(10) res0: Int = 11 

運行時從這個函數字面量建立出來的函數值(對象)被稱爲閉包。該名稱源於「捕獲」其自由變量從而「閉合」該函數字面量的動做。沒有自由變量的函數字面量,好比(x: Int) => x + 1,稱爲閉合語(這裏的語指的是一段源代碼)。所以,運行時從這個函數字面量建立出來的函數值嚴格來講並非一個閉包,由於(x: Int) => x + 1按照目前這個寫法已是閉合的了。而運行時從任何帶有自由變量的函數字面量,好比(x: Int) => x + more建立的函數,按照定義,要求捕獲到它的自由變量more的綁定。相應的函數值結果(包含指向被捕獲的more變量的引用)就被稱爲閉包,由於函數值是經過閉合這個開放語的動做產生的。

這個例子帶來一個問題:若是more在閉包建立之後被改變會發生什麼?在Scala中,答案是閉包可以看到這個改變,參考下面的例子:

scala> more = 9999
more: Int = 9999

scala> addMore(10)
res1: Int = 10009

很符合直覺的是,Scala的閉包捕獲的是變量自己,而不是變量引用的值。正如前面示例所展現的,爲(x: Int) => x + more建立的閉包可以看到閉包外對more的修改。反過來也是成立的:閉包對捕獲到的變量的修改也能在閉包外被看到。參考下面的例子:

scala> val someNumbers = List(-11, -10, -5, 0, 5, 10) someNumbers: List[Int] = List(-11, -10, -5, 0, 5, 10) scala> var sum = 0 sum: Int = 0 scala> someNumbers.foreach(sum += _) scala> sum res3: Int = -11 

這個例子經過遍歷的方式來對List中的數字求和。sum這個變量位於函數字面量sum += _的外圍做用域,這個函數將數字加給sum。雖然運行時是這個閉包對sum進行的修改,最終的結果-11仍然能被閉包外部看到。

那麼,若是一個閉包訪問了某個隨着程序運行會產生多個副本的變量會如何呢?例如,若是一個閉包使用了某個函數的局部變量,而這個函數又被調用了屢次,會怎麼樣?閉包每次訪問到的是這個變量的哪個實例呢?

答案是:閉包引用的實例是在閉包被建立時活躍的那一個。參考下面的函數,函數建立並返回more閉包的函數

def makeIncreaser(more: Int) = (x: Int) => x + more 

該函數每調用一次,就會建立一個新的閉包。每一個閉包都會訪問那個在它建立時活躍的變量more

scala> val inc1 = makeIncreaser(1) inc1: Int => Int = $$Lambda$1269/1504482477@1179731c scala> val inc9999 = makeIncreaser(9999) inc9999: Int => Int = $$Lambda$1269/1504482477@2dba6013 

當調用makeIncreaser(1)時,一個捕獲了more的綁定值爲1的閉包就被建立並返回。同理,當調用makeIncreaser(9999)時,返回的是一個捕獲了more的綁定值9999的閉包。當你將這些閉包應用到入參時,其返回結果取決於閉包建立時more的定義

scala> inc1(10)
res4: Int = 11

scala> inc9999(10)
res5: Int = 10009

這裏,more是某次方法調用的入參,而方法已經返回了,不過這並無影響。Scala編譯器會從新組織和安排,讓被捕獲的參數在堆上繼續存活。這樣的安排都是由編譯器自動完成的,使用者並不須要關心

Scala Collection(集合)

scala中:: , +:, :+, :::, +++的區別

package test
/**
* scala中的:: , +:, :+, :::, +++, 等操做;
*/
object listTest {
def main(args: Array[String]): Unit = {
val list = List(1,2,3)
// :: 用於的是向隊列的頭部追加數據,產生新的列表, x::list,x就會添加到list的頭部
println(4 :: list) //輸出: List(4, 1, 2, 3)
// .:: 這個是list的一個方法;做用和上面的同樣,把元素添加到頭部位置; list.::(x);
println( list.:: (5)) //輸出: List(5, 1, 2, 3)
// :+ 用於在list尾部追加元素; list :+ x;
println(list :+ 6) //輸出: List(1, 2, 3, 6)
// +: 用於在list的頭部添加元素;
val list2 = "A"+:"B"+:Nil //Nil Nil是一個空的List,定義爲List[Nothing]
println(list2) //輸出: List(A, B)
// ::: 用於鏈接兩個List類型的集合 list ::: list2
println(list ::: list2) //輸出: List(1, 2, 3, A, B)
// ++ 用於鏈接兩個集合,list ++ list2
println(list ++ list2) //輸出: List(1, 2, 3, A, B)
}
}
  • :: 該方法被稱爲cons,意爲構造,向隊列的頭部追加數據,創造新的列表。用法爲 x::list,其中x爲加入到頭部的元素,不管x是列表與否,它都只將成爲新生成列表的第一個元素,也就是說新生成的列表長度爲list的長度+1(btw, x::list等價於list.::(x))

  • :++: 二者的區別在於:+方法用於在尾部追加元素,+:方法用於在頭部追加元素,和::很相似,可是::能夠用於pattern match ,而+:則不行. 關於+::+,只要記住冒號永遠靠近集合類型就OK了。

  • ++ 該方法用於鏈接兩個集合,list1++list2

  • ::: 該方法只能用於鏈接兩個List類型的集合

具體示例

  1. scala> "A"::"B"::Nil
  2. res0: List[String] = List(A, B)
  3. scala> "A"+:"B"+:Nil
  4. res1: List[String] = List(A, B)
  5.   scala> Nil:+"A":+"B"
  6. res2: List[String] = List(A, B)
     
  7. scala> res0 ++ res1
  8. res3: List[String] = List(A, B, A, B)
     
  9. scala> res0 ::: res1
  10. res4: List[String] = List(A, B, A, B)
  11. scala> res0 :: res1
  12. res5: List[java.io.Serializable] = List(List(A, B), A, B)
引用

Scala程序設計(第2版)

scala字符串前加s使用$

 

https://my.oschina.net/u/2000675/blog/1592140

字符串中的變量替換,Scala中基礎的字符串插值就是在字符串前加字幕‘s’,而後在字符串中放入變量,每一個變量都應以‘$’開頭。字符串前加字母‘s’時,實際上是在建立一個處理字符串字面量

 

package demo object Demo12 { def main(args:Array[String])={ var name = "zhangsan" var age = 15 println(s"name=$name,age=$age") } }

結果

name=zhangsan,age=15 

在字符串字面量中使用表達式,「${}內可嵌入任何表達式」,包括等號表達式。

package demo object Demo12 { def main(args:Array[String])={ var name = "zhangsan" var age = 15 println(s"name=$name,age=$age") println(s"name=$name,age=${age+1}") } }

結果

name=zhangsan,age=16 

printf格式化

package demo object Demo12 { def main(args:Array[String])={ var name = "zhangsan" var age = 15 println(s"name=$name,age=$age") println(s"name=$name,age=${age+1}") var score = 89.5f printf(f"name=$name,age=${age+1},score=$score%.2f") } }

結果

name=zhangsan,age=15 name=zhangsan,age=16 name=zhangsan,age=16,score=89.50

 特殊符號的理解

// src/main/scala/progscala2/rounding/TryCatchArm.scala
package progscala2.rounding
import scala.language.reflectiveCalls
import scala.util.control.NonFatal
object manage {
def apply[R <: { def close():Unit }, T](resource: => R)(f: R => T) = {
var res: Option[R] = None
try {
res = Some(resource) // 只會引用"resource"一次!!
f(res.get)
} catch {
case NonFatal(ex) => println(s"Non fatal exception! $ex")
} finally {
if (res != None) {
println(s"Closing resource...")
res.get.close
}
}
}
}
object TryCatchARM {
/** Usage: scala rounding.TryCatch filename1 filename2 ... */
def main(args: Array[String]) = {
args foreach (arg => countLines(arg))
}
import scala.io.Source
def countLines(fileName: String) = {
println() // 打印空白行,以增長可讀性
manage(Source.fromFile(fileName)) { source =>
val size = source.getLines.size
println(s"file $fileName has $size lines")
if (size > 20) throw new RuntimeException("Big file!")
}
}
}

manage.apply方法
manage.apply 方法聲明看上去很是奇怪,爲了可以理解該聲明,咱們將對其進行分解。下
面再次列出了該方法的簽名,咱們將分行顯示方法簽名,而每一行也都提供了對應的註釋。

def apply[
R <: { def close():Unit }, ➊
T ] ➋
(resource: => R) ➌
(f: R => T) = {...} ➍

➊這行出現了兩個新的事物。R 表示咱們將要管理的資源類型。而<: 則意味着R 屬於某其餘類型的子類。在本例中R 的父類型是一個包含close():Unit 方法的結構類型。爲了能幫助你更直觀的理解R 類型,尤爲是當你以前沒接觸過結構化類型時,你可以認爲R <: Closable 表示Closable 接口中定義了close():Unit 方法而且R 實現了Closable 接口。不過結構化類型容許咱們使用反射機制嵌入包含close():Unit 方法的任意類型(如Source 類型)。反射機制會形成許多系統開銷,而結構化類型代價也較爲昂貴, 所以就像後綴表達式那樣,Scala 將反射列爲可選特性,爲了可以讓編譯器相信咱們知道咱們在作什麼,須要在代碼中添加import 語句。➋ 咱們傳入了用於處理資源的匿名函數,而T 表示該匿名函數的返回值。➌ 儘管看上去像是一個聲明體較爲奇特的函數,但resource 事實上是一個傳名參數(bynameparameter)。咱們暫且將其視爲一個在調用時應省略括號的函數。➍ 最後咱們將傳入第二個參數列表,其中包含了一個輸入爲resource、返回值類型爲T的匿名函數,該匿名函數將負責處理resource。咱們再回到註釋1,假設全部資源均實現了Closable 抽象,那麼apply 方法聲明看起來會是下面這個樣子:object manage {def apply[ R <: Closable, T](resource: => R)(f: R => T) = {...}...}resource 只會在val res = Some(resource) 這行代碼中被求值, 這行代碼必不可少的。因爲resource 的表現與函數類似,所以就像是一個會被重複調用的函數,每次引用該變量時便會對其求值。但咱們並不但願每次引用resource 時都會執行一次Source.fromFile(fileName),由於這意味着咱們每次都會從新打開一個新的Source 實例。以後,咱們將res 值傳入工做函數f 中。

相關文章
相關標籤/搜索