Scalahtml
Author: Lijbjava
Email: lijb1121@163.comes6
Scala是一門多範式的編程語言,同時支持面向對象和麪向函數編程風格。它以一種優雅的方式解決現實問題。雖然它是強靜態類型的編程語言,可是它強大的類型推斷能力,使其看起來就像是一個動態編程語言同樣。Scala語言最終會被翻譯成java字節碼文件,能夠無縫的和JVM進行集成,同時可使用Scala調用java的代碼庫。express
編程指南:https://docs.scala-lang.org/tour/tour-of-scala.html編程
Scala環境搭建數組
下載scala:https://www.scala-lang.org/download/2.11.12.htmlapp
Windows版本安裝dom
點擊scala-2.11.12.msi傻瓜安裝編程語言
配置Scala的環境變量SCALA_HOME變量ide
SCALA_HOME=C:\Program Files (x86)\scala PATH=%SCALA_HOME%/bin
打開命名窗口
C:\Users\HIAPAD>scala Welcome to Scala 2.11.12 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_161). Type in expressions for evaluation. Or try :help.
scala>
CentOS安裝
下載scala-2.11.12.rpm
安裝配置Scala
[root@CentOS ~]# rpm -ivh scala-2.11.12.rpm Preparing... ########################################### [100%] 1:scala ########################################### [100%] [root@CentOS ~]# scala Welcome to Scala 2.11.12 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_171). Type in expressions for evaluation. Or try :help.
IDEA集成Scala開發環境
Scala變量
參考:https://docs.scala-lang.org/resources/images/tour/unified-types-diagram.svg
scala> var i=1 i: Int = 1 scala> var i="abc" i: String = abc scala> var i: ="abc" <console>:11: error: type mismatch; found : String("abc") required: Int var i:Int="abc" ^
Scala全部的變量在聲明時能夠省略類型,由編譯器自動推斷,一旦編譯後,類型固定,不能夠更改
scala> var i=1 i: Int = 1 scala> i="abc" <console>:12: error: type mismatch; found : String("abc") required: Int i="abc" ^ scala> var i:Int=1:Int i: Int = 1
var和val區別?
var修飾的變量值是能夠改變的
val修飾的值,不能夠改變相似於java中final
scala> val i=10 i: Int = 10 scala> i=1000 <console>:12: error: reassignment to val i=1000 ^
字面值類型相互轉換
scala> val face: Char = '☺' face: Char = ☺ scala> val i:Int =face i: Int = 9786 scala> val c:Char = i.toChar c: Char = ☺
變量定義
var|val 變量名[:變量類型]=變量值[:變量類型] var|val 變量名 = 變量值 //使用Scala的類型推斷 var a:Int = 1 var str:String = "hello world" var pair:(Int,String) = (1,"測試") //元組 var b=1 var i:Int=1 var j=1:Byte var tuple=(1,2)//元組
元組在Scala中是由多種類型的變量組裝而成,一旦賦值,不可更改
scala> var pair:(String,Int)= "zhangsan" -> 10 pair: (String, Int) = (zhangsan,10) scala> var pair:(String,Int)= ("zhangsan", 10) pair: (String, Int) = (zhangsan,10) scala> var pair= ("zhangsan", 10) pair: (String, Int) = (zhangsan,10) scala> var pair= "zhangsan" -> 10 pair: (String, Int) = (zhangsan,10) scala> var tuple:(Int,Int,String)=(1,2,"Hello") scala> tuple._1 res4: Int = 1 scala> tuple._2 res5: Int = 2 scala> tuple._3 res6: String = Hello scala> tuple._3="world" <console>:12: error: reassignment to val tuple._3="world" ^
算術運算符
算術運算符:+、-、*、/、%、
關係運算符:==、!=、>、<、>=、<=
邏輯運算符:&&、||、!
位運算符:&(按位與)、|(按位或)、^(異或)、~(取反)、<<、>>、>>>(無符號)
賦值運算符:= 、
組合賦值:(算術|位運算=)
判斷Scala的類型
var i=0 var sex:Any={ if(i>0){ true }else{ "不知道" } } if(sex.isInstanceOf[Boolean]){ println("你是布爾") }else{ println("你是String:"+sex.asInstanceOf[String]) }
Scala的分支語句
if條件分支
if(布爾表達式 1){ // 若是布爾表達式 1 爲 true 則執行該語句塊 }else if(布爾表達式 2){ // 若是布爾表達式 2 爲 true 則執行該語句塊 }else if(布爾表達式 3){ // 若是布爾表達式 3 爲 true 則執行該語句塊 }else { // 若是以上條件都爲 false 執行該語句塊 }
while循環控制
//求1到100的和 var i=0 //可省略 var n=0 //可省略 while(i< 100){ i+=1 n+=i } print("while循環後結果爲:"+n)
do-while循環
//求1到100的和 var i=0 //可省略 var n=0 //可省略 do{ i+=1 n+=i }while(i<100) print("do->while循環後結果爲:"+n)
for循環
//求1到100的和 var i=0 //可省略 var n=0 //可省略 for(i<-1 to 100 by 2){ n+=i } println(n)
for 循環 中你可使用分號 (;) 來設置多個區間,它將迭代給定區間全部的可能值(實現嵌套)
//100之內能被3整除不能被5整除的和 var i=1 var result=0 //可省略 for(i<-1 to 100 if i%3==0 && i%5!=0){ result+=i } print(result)
for循環集合
val list = List(1,2,3,4,5,6) for (a<-list){ println(a) }
for循環過濾
//例一:百錢買百雞 var x = 0 //可省略 var y = 0 //可省略 for (x <- 0 to 33) { var maxy = (100 - x * 3) / 2 for (y <- 0 to maxy) { var z = 100 - x - y; if (3 * x + 2 * y + z / 3.0 == 100 && x + y + z == 100) { println("公雞:" + x + "只;" + "母雞:" + y + "只;" + "小雞:" + z + "只。"); } } } //例二: var a = 0; //可省略 val list = List(1,2,3,4,5,6,7,8,9,10); for(a<-list if a%2==0;if a<=4){ println(a) }
for使用 yield
//yied至關於提取出 var a = 0; val numList = List(1,2,3,4,5,6,7,8,9,10); // for 循環 var retVal = for{ a <- numList if a != 3; if a < 8 }yield a // 輸出返回值 for( a <- retVal){ println( "Value of a: " + a );、 }
match-case
Scala中取消了switch-case語法,取而代之的是使用match-case語法,也稱爲模式匹配。
var sex="..." var alias =sex match { case "boy" => "男孩" case "girl" => "女孩" case default => "怪物" } println(alias)
default能夠用_替換
Break語法 Scala 語言中默認是沒有 break 語句,可是你在 Scala 2.8 版本後可使用另一種方式來實現 break 語句。當在循環中使用 break 語句,在執行到該語句時,就會中斷循環並執行循環體以後的代碼塊。Scala 中 break 的語法有點不大同樣,格式以下:
var a = 0; val numList = List(1,2,3,4,5,6,7,8,9,10); val break = new Breaks break.breakable( for (a<-numList){ println( "Value of a: " + a ) if(a==4){ break.break() } } ) println( "After the loop" );
Scala函數
函數聲明
def functionName ([參數列表]) : [return type]
標準函數
def sum(x:Int,y:Int):Int={ return x+y } //能夠嘗試省略返回值類型 def multi(x:Int,y:Int)={ x*y } def sayHi(name:String):Unit={ println("hi~ "+name) }
指定函數名
def main(args: Array[String]): Unit = { println(sum(5,2)) } def sum (x:Int,y: Int): Int ={ return x+y }
可變長參數函數
要求必須放置在因此參數的最後
def main(args: Array[String]): Unit = { println(sum(1,2,3,4,5,6,7,8,9,10))//結果:55 } def sum(args: Int*): Int = { var result = 0 for (i <- args) { result += i } return result } //打印參數WrappedArray(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) 說明傳入的參數是個一個args數組
默認參數值
def main(args: Array[String]) { sayHi("ww") sayHi() } def sayHi(name:String="zhangsan"):Unit={ println("hi~ "+name) }
內嵌函數
def main(args: Array[String]): Unit = { println(factorial(5)) } def factorial (x:Int)={ def mulit(i:Int):Int={ if(i>0){ i*mulit(i-1) }else{ 1 } } mulit(x) }
匿名函數
匿名函數 Scala 中定義匿名函數的語法很簡單,箭頭左邊是參數列表,右邊是函數體。使用匿名函數後,咱們的代 碼變得更簡潔了。本質是一個lambdar表達式
(x:Int,y:Int) => {x+y}
將一個函數變成變量
//函數 def fun(x:Int):Int={ return x+1 } //改寫成變量 var f1:(Int)=>Int=(x)=>x+1 val f2 = (x: Int) => x + 1
函數的返回值問題
1,聲明函數的返回值類型,則在函數返回時必定要符合返回類型
2,能夠不聲明函數返回值類型,scala解釋器會自動推斷返回的數據類型
3,遞歸函數必須顯式聲明返回的類型
4,定義函數時沒有等號= 則沒有返回值。這類函數至關於一個執行過程
高階函數
在數學和計算機科學中,高階函數是至少知足下列一個條件的函數
在數學中它們也叫作算子(運算符)或泛函。微積分中的導數就是常見的例子,由於它映射一個函數到另外一個函數。
//求給定區間的整數和 def main(args: Array[String]): Unit = { println(sumInts(2,5)) } def sumInts(a: Int, b: Int): Int = if(a > b) 0 else a + sumInts(a + 1, b) } //求給定區間連續的整數的平方和 def main(args: Array[String]): Unit = { println(sumSquare(1,100))//結果爲338350 } def square(x: Int) = x * x def sumSquare(a:Int,b:Int):Int={ if(a > b) 0 else square(a)+sumSquare(a+1,b) }
map方法將一個函數應用到某個集合的全部元素並返回結果;foreach將函數應用到每一個元素。
(1 to 9).map("^" * _).foreach(println _) ^^ ^^^ ^^^^ ^^^^^ ^^^^^^ ^^^^^^^ ^^^^^^^^ ^^^^^^^^^
filter方法輸出全部匹配某個特定條件的元素:
(1 to 9).filter(_ % 2 == 0).foreach(println _) //結果:2 4 6 8
函數柯里化(Currying)
柯里化(Currying)指的是將原來接受兩個參數的函數變成新的接受一個參數的函數的過程。新的函數返回一個以原有第二個參數爲參數的函數。
在上面高階函數的例子中,咱們經過def sumInts(a: Int, b: Int): Int = sum(x => x, a, b)、def sumSquared(a: Int, b: Int): Int = sum(x => x*x, a, b)來定義新的函數,上面這兩個函數每次都要傳入a和b兩個參數到sum函數中,咱們可否簡化這些參數使得函數定義更簡單呢?
咱們能夠經過返回函數的函數來簡化參數:
/*當你調用sum1(2)(8)時,其實是依次調用兩個普通函數(非柯里化函數), 第一次調用使用一個參數x,返回一個函數類型的值,第二次使用參數y調用這個函數類型的值*/ def sum(f: Int => Int): (Int, Int) => Int = { def sumF(a: Int, b: Int): Int = if(a > b) 0 else f(a) + sumF(a+1, b) sumF } //因而獲得以下定義,這樣就簡化了參數 def sumInts = sum(x => x) def sumSquared = sum(x => x * x) def sumPowersOfTwo = sum(powerOfTwo)
根據上面的例子,咱們能不能更加簡化,省去sumInts、sumSquared、sumPowersOfTwo這幾個中間函數的形式呢?
經過sum(square)(1, 10)函數來替代sumSquared函數。
通常狀況,這類函數的左結合的:sum(square)(1, 10) == (sum(square))(1, 10)。
咱們能夠這樣定義sum函數:
def sum(f: Int => Int)(a: Int, b: Int): Int = if(a > b) 0 else f(a) + sum(f)(a + 1, b)
這使得函數編寫更加簡潔。
通常的,多參數函數定義爲def f(args1)...(argsn) = E,
當n > 1時,等同於def f(args1)...(args n-1) = {def g(argsn) = E; g}或者def f(args1)...(args n-1) = (argsn => E)。
若是重複這個過程n次,獲得def f = (args1 => (args2 => ... (argsn => E) ) )。
這種函數定義稱爲柯里化(Currying)。
類和object
Scala中的類是用於建立對象的藍圖,其中包含了方法、常量、變量、類型、對象、特質、類,這些統稱爲成員。因爲Scala沒有靜態方法和靜態類,經過object去定義靜態方法或者靜態對象。當object和Class放在一個文件中時候稱該object爲伴生對象。
object(單例類)
object UserService { def main(args: Array[String]): Unit = { var us1=UserService var us2=UserService println(us1==us2) //結果爲true-->說明us1和us2是同一個對象-->單例 UserService.sayHi() us1.sayHi() us2.sayHi() sayHi() } def sayHi():Unit={ println("hello") } }
類定義
class Student { var id=1 var name="zhangsan" var age=18 var birtDay=new Date() }
構造器
在scala中只有Class或者abstract Class纔能有構造方法,Scala中聲明在Class上的構造必須爲最簡構造,其餘構造方法參數必須涵蓋最簡構造。
class User{ var id:Int = _ var name:String = _ def this(id:Int,name:String){ this() this.id=id this.name=name } def sayHi(): Unit ={ println(id+" "+name) } } ----- class User(var id:Int,var name:String){ def this(){ this(1,"張三") } def sayHi(): Unit ={ println(id+" "+name) } }
伴生對象
當類名和單例類名字同樣的時候,咱們把單例類稱爲伴生對象
class User{ } object User { def main(args: Array[String]): Unit = { var u1=User var u2=User var u3=new User() println(u1==u2)//true println(u1==u3)//false } }
apply&unapply
Scala中的 apply 方法有着不一樣的含義, 對於函數來講該方法意味着調用function自己, 如下說明摘自
Programming in Scala, 3rd Edition
Every function value is an instance of some class that extends one of several FunctionN traits in package scala, such as Function0 for functions with no parameters, Function1 for functions with one parameter, and so on. Each FunctionN trait has an apply method used to invoke the function.
在Scala語言中, 函數也是對象, 每個對象都是scala.FunctionN(0-22)的實例, 其中N是函數參數的數量, 例如咱們定義一個函數並賦值給一個變量:
def main(args: Array[String]): Unit = { println(fun(2)) //結果爲2 var fun1:(Int)=>Int=(x)=>x+1 val fun2 = (x: Int) => x + 1 println(fun1.apply(2)) //結果爲2 println(fun2.apply(2)) //結果爲2 } def fun(x:Int):Int={ return x+1 }
apply 方法用在object中通常做爲工廠方法用於產生Class對象,一般,在一個類的半生對象中定義apply方法,在生成這個類的對象時,就省去了new關鍵字。
class Student (name:String) { } object Student{ def apply(name: String): Student = new Student(name) def main(args: Array[String]): Unit = { var s1=new Student("張三") var s2= Student("李四") var s3=Student("李四") println(s1) //com.baizhi.zpark.Student@47f37ef1 println(s2) //com.baizhi.zpark.Student@5a01ccaa println(s3) //com.baizhi.zpark.Student@71c7db30 println(s1==s2) //false println(s2==s3) //false } }
extractor object是具備unapply方法的對object。unapply方法能夠看作是apply方法的相反操做,apply方法接受構造參數變成對象,而unapply方法接受一個對象,從中提取值。
object CustomerID { def apply(name: String) = s"$name--${Random.nextLong}" def unapply(customerID: String): Option[String] = { val stringArray: Array[String] = customerID.split("--") if (stringArray.tail.nonEmpty) Some(stringArray.head) else None } } val customer2ID = CustomerID("Nico") val CustomerID(name) = customer2ID println(name) // prints Nico
extractor object 更多可參考 https://docs.scala-lang.org/tour/extractor-objects.html
抽象類/抽象方法
//抽象方法 abstract class Animal(name:String) { def eat():Unit={ println("animal can eat...") } def sleep():String } //集成抽象類--覆蓋裏面的抽象方法 object AnimalImpl extends Animal { override def sleep(): Unit = { println("Animal sleeping 5 hours") } def main(args: Array[String]): Unit = { sleep() //調用覆蓋的方法 eat() //調用繼承的抽象類中的非抽象方法 } }
特質trait(java中的接口)
trait Speakable { def speek():Unit } trait Flyable{ def fly():Unit }
繼承&實現
類-類、類接口
class Dog(name:String) extends Animal(name:String) with Speakable { override def sleep(): String = { "i'm a dog I sleep 8 hours" } override def speek(): Unit = { println("wang wang ~~") } override def eat(): Unit = { println("啃骨頭") } } object Dog{ def apply(name: String): Dog = new Dog(name) def main(args: Array[String]): Unit = { var dog=Dog("小花") dog.eat() } } class Bird extends Flyable with Speakable { override def fly(): Unit = { println("i can fly") } override def speek(): Unit = { println("唧唧咋咋") } } object Bird{ def apply(): Bird = new Bird() def main(args: Array[String]): Unit = { var bird=Bird() bird.fly() } }
接口-接口
trait A{} trait C{} trait B extends A with C{}
接口動態植入
class Bird extends Flyable {//Flyable是trait override def fly(): Unit = { println("i can fly") } } object Bird{ def main(args: Array[String]): Unit = { var bird=new Bird() with Speakable { override def speek(): Unit = { println("唧唧咋咋") } } bird.speek() } }
注意在覆蓋有實現的方法必須添加overwrite一個類只能繼承一個類with多個trait例如
Class A extends B with C with D{ }
上述的B多是特質也能夠是抽象類 但C、D必須是特質。
this&self-type
和java有所不一樣在Scala中class和object均可以使用this關鍵字表明自身。同時Scala支持給當前對象起別名
class Student(name:String) { self => //起別名 用self表明當前對象 def sayHello(): Unit ={ println(this == self) println("hello "+self.name) } } object Student{ def apply(name: String): Student = new Student(name) def main(args: Array[String]): Unit = { var stu=Student("zhangsan") stu.sayHello() } }
self-type是一種聲明Trait必須混合到另外一個Trait的方法,即便它沒有直接擴展它。這使得依賴的成員能夠在沒有導入的狀況下使用。
定義:特質能夠要求混入它的類擴展自另外一個類型,可是當使用自身類型(self type)的聲明來定義特質時(this: ClassName =>),這樣的特質只能被混入給定類型的子類當中。 若是嘗試將該特質混入不符合自身類型所要求的類時,就會報錯。
從技術角度上看,自身類型是在類中提到this時,對於this的假設性類型。
從實用角度上看,自身類型指定了對於特質可以混入的具體類的需求。若是你的特質僅用於混入另外一個或幾個特質,那麼能夠指定那些假設性的特質。
trait User { def username: String } trait Tweeter { this: User => // reassign this def tweet(tweetText: String) = println(s"$username: $tweetText") } class VerifiedTweeter(val username_ : String) extends Tweeter with User { // We mixin User because Tweeter required it def username = s"real $username_" } val realBeyonce = new VerifiedTweeter("Beyoncé") realBeyonce.tweet("Just spilled my glass of lemonade")
可見性控制
Scale和java類型能夠控制Scala中對象的可見性,例以下面表示建立一個公開類,任何包下的類均可以使用,同時該類下的成員變量都是公開。
class Student { var id=1 var name="zhangsan" var age=18 var birtDay=new Date() }
private限定 private修飾的屬性只能對本類可使用,對於伴生對象可見 private修飾類,可繼承而且繼承以後能夠訪問父類屬性和方法,除非父類的屬性和方法加上private修飾符以後不可訪問
class Student { private var id=1 var name="zhangsan" var age=18 var birtDay=new Date() } object Student{ def apply(): Student = new Student() def main(args: Array[String]): Unit = { val student = Student() student.id } }
protected限定 protected修飾的屬性對本類/伴生對象可用,對於子類/子類的伴生對象可見
class Student { protected var id=1 var name="zhangsan" var age=18 var birtDay=new Date() } object Student{ def apply(): Student = new Student() } class SmallStudent extends Student{ } object SmallStudent { def apply(): SmallStudent = new SmallStudent() def main(args: Array[String]): Unit = { val student = SmallStudent() student.id } }
this限定 若是嚴格限定屬性能夠操做範圍僅僅限制在本類,去除伴生對象的可見性,能夠添加this限定
class Student { protected|private[this] var id=1 var name="zhangsan" var age=18 var birtDay=new Date() } object Student{ def apply(): Student = new Student() }
包限定 Scala還能夠作包限定,表示id屬性對Student的子類可見,其餘類必須是同包下可見
package com.jiangzz.demo import java.util.Date class Student { protected[demo] var id=1 var name="zhangsan" var age=18 var birtDay=new Date() } object Student{ def apply(): Student = new Student() }
final限定
final能夠修飾Class和方法,final修飾的類不能夠被繼承,修飾的方法不能夠被覆蓋,可是final修飾屬性時沒有效果,val修飾屬性才時不可變的。
final class Student(name:String) { final def sayHi(name:String):String={ "hello "+name } }
方法覆蓋&重載 scala保留了java中的方法重載和覆蓋功能具體使用和Java語法相似。
def fun(x:Int,y:Int):Int={ x+y } def fun(x:String,y:Int):String={ x+y } override def toString: String = { "..." }
函數對象(重點)
Partial Applied Function(部分應用函數)
函數對象這是一種特殊的對象,也有人稱之爲高階函數。咱們把不具備任何可改變狀態的對象稱爲函數式對象。對於函數而言分爲參數類型和函數體實現,Scala的函數對象就是一種特殊的class既能夠擔當函數計算職責又能夠做爲變量傳遞。Scala能夠很方便的將函數轉變成爲函數對象。
scala> def sum(v1:Int,v2:Int):Int={ | return v1+v2 | } sum: (v1: Int, v2: Int)Int scala> val s1=sum(_:Int,1) s1: Int => Int = $$Lambda$1291/1237850567@17d28c1d scala> s1(3) res12: Int = 4 scala> val s2=sum _ s2: (Int, Int) => Int = $$Lambda$1158/879220032@67710b92
函數對象
一般將s1和s2稱爲sum函數的部分應用函數(Partial Applied Function),仔細觀察能夠發現當sum轉變爲s一、s2對於Scala而言都是某種對象類型的變量。
對於s1而s1的類型就是Int=>Int類型變量;s2而言該s2的類型就是(Int,Int)=>Int類型。對於Scala而言Scala在作方法解析的時候底層會將方法轉換爲Function1~22對象類型,當程序編寫一個方法事實上是在建立Function[1~22]對象,調用對象的方法就等價於調用Function的apply方法
scala> def sum(v1:Int,v2:Int):Int={ | return v1+v2 | } sum: (v1: Int, v2: Int)Int scala> val s=sum _ s: (Int, Int) => Int = $$Lambda$1295/814452367@1faa627c scala> s.isInstanceOf[Function2[Int,Int,Int]] res20: Boolean = true scala> s.isInstanceOf[(Int,Int)=>Int] res21: Boolean = true scala> s.apply(1,2) res0: Int = 3
以上代碼等價於
scala> val s=new Function2[Int,Int,Int] { | override def apply(v1: Int, v2: Int): Int = { | return v1+v2 | } | } s: (Int, Int) => Int = <function2> scala> s.apply(1,2) res11: Int = 3
經過函數到對象的轉換,咱們不難發現,能夠將任意一個函數類型轉換爲變量類型,函數轉換對象類型的變量轉換規則以下:
按照上述轉換規則 將函數聲明轉換爲變量類型,將函數實現用lambda表達式實現就能夠實現將函數轉變Function類型的變量。
class SumFunction extends ((Int,Int)=>Int){ override def apply(v1: Int, v2: Int): Int = { return v1 + v2 } } object SumFunction{ def main(args: Array[String]): Unit = { val s=new SumFunction() println(s.apply(1,2)) println(s.isInstanceOf[Function2[Int,Int,Int]]) println(s.isInstanceOf[(Int,Int)=>Int]) } }
使用場景以下
class Student(val name:String) { def map(f:Student=>String):String={ return f(this) } } object Student{ def apply(name: String): Student = new Student(name) def main(args: Array[String]): Unit = { val stu=Student("張三") var str=stu.map(x=>x.name) println(str) } }
分析:
複雜的函數對象編程
class Student(val name:String) { def map1(f:Student=>String):String={ return f(this) } val map2:(Student => String) => String = (x:(Student=>String))=> x.apply(this) } object Student{ def apply(name: String): Student = new Student(name) def main(args: Array[String]): Unit = { var s:Student=Student("王五") s.map1(x=>x.name) s.map2(x=>x.name) } }
PartitalFunction(偏函數) -瞭解
偏函數主要適用於處理指定類型的據,一般用於集合處理中。
def main(args: Array[String]): Unit = { Array(1,2,3,"a","b","c").collect(pf1).foreach(println) } //自定義偏函數 val pf1 = new PartialFunction[Any, Int] { override def isDefinedAt(x: Any): Boolean = { if(x.isInstanceOf[Int]) true else false } override def apply(v1: Any): Int = { v1.asInstanceOf[Int] } } //case 實現偏函數 val pf2 :PartialFunction[Any, Int]={ case x:Int => x+1 }
參考:https://blog.csdn.net/bluishglc/article/details/50995939
高階函數
--->【摘自官方文檔】
高階函數將其餘函數做爲參數或做爲結果返回函數。這是可能的,由於函數是Scala中的第一類值。這個術語在這一點上可能會有點混亂,咱們對於將函數做爲參數或返回函數的方法和函數使用短語「高階函數」。
其中一個最多見的例子是mapScala中可用於集合的高階函數。
val salaries = Seq(20000, 70000, 40000) val doubleSalary = (x: Int) => x * 2 val newSalaries = salaries.map(doubleSalary) // List(40000, 140000, 80000)
doubleSalary是一個函數,它接受一個Int x,並返回x * 2。一般,箭頭左側的元組=>是參數列表,右側表達式的值是返回的值。在第3行,該函數doubleSalary應用於工資列表中的每一個元素。
要縮小代碼,咱們可使函數匿名並將其做爲參數直接傳遞給map:
val salaries = Seq(20000, 70000, 40000) val newSalaries = salaries.map(x => x * 2) // List(40000, 140000, 80000)
注意x在上面的例子中如何不聲明爲Int。那是由於編譯器能夠根據函數映射的類型推斷出類型。編寫同一段代碼的更爲慣用的方法是:
val salaries = Seq(20000, 70000, 40000) val newSalaries = salaries.map(_ * 2)
因爲Scala編譯器已經知道參數的類型(單個Int),所以您只須要提供函數的右側。惟一須要注意的是,您須要使用_代替參數名稱(它x在前面的示例中)。
也能夠將方法做爲參數傳遞給高階函數,由於Scala編譯器會將該方法強制轉換爲函數。
case class WeeklyWeatherForecast(temperatures: Seq[Double]) { private def convertCtoF(temp: Double) = temp * 1.8 + 32 def forecastInFahrenheit: Seq[Double] = temperatures.map(convertCtoF) // <-- passing the method convertCtoF }
這裏將方法convertCtoF傳遞給forecastInFahrenheit。這是可能的,由於編譯器強制convertCtoF執行該函數x => convertCtoF(x)(注意:x將是一個生成的名稱,保證在其範圍內是惟一的)。
使用高階函數的一個緣由是減小冗餘代碼。假設您想要一些能夠經過各類因素提升某人工資的方法。若是不建立高階函數,它可能看起來像這樣:
object SalaryRaiser { def smallPromotion(salaries: List[Double]): List[Double] = salaries.map(salary => salary * 1.1) def greatPromotion(salaries: List[Double]): List[Double] = salaries.map(salary => salary * math.log(salary)) def hugePromotion(salaries: List[Double]): List[Double] = salaries.map(salary => salary * salary) }
請注意三種方法中的每一種方法如何僅因乘法因子而異。爲簡化起見,您能夠將重複的代碼提取到更高階的函數中,以下所示:
object SalaryRaiser { private def promotion(salaries: List[Double], promotionFunction: Double => Double): List[Double] = salaries.map(promotionFunction) def smallPromotion(salaries: List[Double]): List[Double] = promotion(salaries, salary => salary * 1.1) def bigPromotion(salaries: List[Double]): List[Double] = promotion(salaries, salary => salary * math.log(salary)) def hugePromotion(salaries: List[Double]): List[Double] = promotion(salaries, salary => salary * salary) }
新方法promotion採用工資加上類型函數Double => Double (即採用Double並返回Double的函數)並返回產品。
在某些狀況下,您要生成一個函數。這是一個返回函數的方法示例。
def urlBuilder(ssl: Boolean, domainName: String): (String, String) => String = { val schema = if (ssl) "https://" else "http://" (endpoint: String, query: String) => s"$schema$domainName/$endpoint?$query" } val domainName = "www.example.com" def getURL = urlBuilder(ssl=true, domainName) val endpoint = "users" val query = "id=1" val url = getURL(endpoint, query) // "https://www.example.com/users?id=1": String
注意urlBuilder的返回類型(String, String) => String。這意味着返回的匿名函數須要兩個字符串並返回一個String。在這種狀況下,返回的匿名函數是(endpoint: String, query: String) => s"https://www.example.com/$endpoint?$query"。
隱式傳值\隱式轉換
隱式值獲取
def main(args: Array[String]): Unit = { implicit var str="張三" //聲明隱式值 val b=implicitly[String] println(b+"你好") //結果:張三你好 }
使用implicitly[類型],必須保證當前上下文有且僅有一個隱式值類型,通常這種隱式值變量的聲明寫在object單例類或者伴生對象中。
要求參數之中只能有一個implicit類型匹配。
implicit val s="您好" def sayHi1(implicit msg:String):Unit={ println(msg) } def sayHi2(name:String)(implicit msg:String):Unit={//柯利化寫法,要求必須做爲最後一個參數傳入 println(msg+" "+name) } def sayHi3(implicit name:String,msg:String):Unit={//標準函數寫法,要求implict修飾的隱式值,必須放在第一位 println(msg+" "+name) } sayHi1 sayHi2("張三") sayHi2("李四")("早上好") sayHi3(implicitly[String],"hello") //不常見
錯誤寫法:
def sayHi4(msg:String,implicit name:String):Unit={ println(msg+" "+name) } def sayHi4(implicit name:String)(msg:String):Unit={ println(msg+" "+name) }
若是是標準函數寫法,要求implict修飾的隱式值,必須放在第一位
若是是柯利化寫法,要求必須做爲最後一個參數傳入
參數隱式轉換
def main(args: Array[String]): Unit = { implicit def str2stu(v:String):Student={ new Student(v) } sayHi2Stu("王五") } def sayHi2Stu(stu:Student):Unit={ println("學生:"+stu.name+" 你好!") }
隱式方法增強
class Student(val name:String) { def study():Unit={ println("學生:"+name+" 學習..") } } object UserImplicts{ implicit class StudentImplicts(stu:Student){//參數給個對象就能給該對象加上該對象不存在的方法 def playGame():Unit={ println("學生 "+stu.name+" 玩遊戲") } } } object Student{ import UserImplicts._ def main(args: Array[String]): Unit = { val s=new Student("張三") s.study s.playGame } }
隱式增強,優先調用對象本身的方法,若是該調用對象自己沒有方法,會嘗試查找編譯的上下文,看當前上下文中是否有對應的隱式增強方法。
Scala 泛型
<:上邊界限定(居多)
trait Keeper[U <: Animal]{ def keep(a:U); }
表示Keeper的實現類只能飼養Animal以及Animal的子類
:下界限定
trait Keeper[U >: Dog]{ def keep(a:U); }
表示Keeper的實現類只能飼養Dog或者Dog的父類
<%視圖限定
implicit def str2Stu(v:String):Student={ new Student(v) } def getStu[T <% Student](v:T): T ={ return v } val stu=getStu("張三")
運行的時候嘗試將T類型隱式轉換爲Student類型
T:A上下文綁定
必須知足A[T]隱式值
class Student[T]() { def map(t:T):String={ t.toString +" world!" } } def main(args: Array[String]): Unit = { implicit var s=new Student[String]() //Student隱士值 say[String]("hello") } def say[T:Student](t:T):Unit={ //[T:Student]:類型參數 (t:T)值參數 var stu=implicitly[Student[T]] stu.map(t) } def f[A:ClassTag](n: Int):Array[A] = { return new Array[A](n) }
上下文中,必須含有Student[String]隱式值,能夠有效防止缺失參數,例如ClassTag該標籤系統已經爲 A:ClassTag建立了ClassTag[A]的隱式轉換,而該隱式轉換時在建立Array數組必需要用的。因此在給數組指定泛型的時候須要加ClassTag
多重界定符
A和B爲T上界
def sayT <: A with B:Unit={ println(v) } trait A{} trait B{}
A和B爲T下界
def sayT >: A with B:Unit={ println(v) } trait A{} trait B{}
同時擁有上界和下界,而且A爲下界,B爲上界,A爲B的子類,順序不能顛倒
def sayT >: A <: B:Unit={ println(v) } trait A{} trait B{}
視圖界定,即同時可以知足隱式轉換的A和隱式轉換的B
def sayT <% A <% B:Unit={ println(v) } trait A{} trait B{}
+A 協變
若是範型裏存在繼承關係,則繼承關係也是能夠有多態的,子類範型賦值給父類範型,協變。
class Covariant[+T](t:T){} val cov = new Covariant[Dog](new Dog("小狗")) val cov2:Covariant[Animal] = cov
-A 逆變
class Covariant[-T](t:T){} val cov = new Covariant[Animal](new Animal("動物")) val cov2:Covariant[Dog] = cov
A 不變
class Covariant[T](t:T){} val cov = new Covariant[Animal](new Animal("動物")) val cov2:Covariant[Animal] = cov
Scala數組|集合
Array
var arr1=Array(1,2,3,4,5) //第一種建立方式 默認調用Array的apply方法 var arr2=new Array[Int](5)//第二種建立方式 指定泛型 println(arr1.apply(1)) arr1.foreach(i=>println(i))//數組遍歷
Range區間
scala> var range=new Range(1,10,2) range: scala.collection.immutable.Range = inexact Range 1 until 10 by 2 scala> var range=Range(1,10 ,2) range: scala.collection.immutable.Range = inexact Range 1 until 10 by 2 scala> var range=0 to 10 by 2 range: scala.collection.immutable.Range = Range 0 to 10 by 2 scala> var range=0 until 10 by 2 range: scala.collection.immutable.Range = Range 0 until 10 by 2
向量
scala> var vector=Vector(1,2,3) vector: scala.collection.immutable.Vector[Int] = Vector(1, 2, 3) scala> var vector=for(i<- 0 to 10 by 2;if(i>4)) yield i vector: scala.collection.immutable.IndexedSeq[Int] = Vector( 6, 8, 10)
Iterator
只能夠遍歷一次
var it=Iterator(1,2,3,4,5,6) it.foreach(x=>println(x)) //第一次遍歷有結果 it.foreach(println) //第二次遍歷沒結果
List集合(不可變集合)
::/+:追加元素
var list =List(1,2,3,4,5) list::=6 //默認追加到集合的最前面 list+:=8 //默認追加到集合的最前面 list.foreach(println) //6,1,2,3,4,5 list(0)=10 //報錯 list不容許修改 println(list(0)) //獲取第一個元素
:::合併集合
var list1=List(1,2,3) var list2=List(4,5,6) list1:::=list2 list1.foreach(println) //把list2合併追加到list1前面獲得新的集合{4,5,6,1,2,3} list2.foreach(println) //4,5,6 list2結果不變
drop
var list2=List(4,5,6,7,8) val ints = list2.drop(2) //刪除前兩個元素返回一個新集合ints ints.foreach(println) //6,7,8 list2.foreach(println) //原來集合元素不變 結果爲:4,5,6,7,8
slice 截取某個區間的數據
var list=List(1,2,3,4,5) list.slice(1,3).foreach(println)//結果爲2,3-->返回(1,3]不包括1,包括3的新集合
reverse(翻轉)
var list=List(1,2,3,4,5) list.reverse.foreach(println) //結果爲:5,4,3,2,1
take/takeRight
var list=List(1,2,3,4,5) list.take(3).foreach(println)//獲取集合前三個元素--> 1,2,3 list.takeRight(3).foreach(println)//獲取集合後三個元素--> 3,4,5
takeWhile
從集合頭部看來是匹配,遇到第一個false則終止
var list=List(1,2,3,4,5) list.takeWhile(x=> x<4).foreach(println) //從頭匹配,遇到第一個false則終止--> 1,2,3
head
獲取集合中第一個元素
var list=List(1,2,3,4,5) println(list.head) //獲取集合中第一個元素-->結果爲:1
tail
獲取集合中除去第一個元素以外的全部元素
var list=List(1,2,3,4,5) println(list.tail)//獲取集合中除去第一個元素以外的全部元素 -->結果爲:(2,3,4,5)
ListBuffer(可變集合)
def main(args: Array[String]): Unit = { var list=ListBuffer[Int]() list.+=(1) list.+=(2,3,4,5) //ListBuffer是可變的集合 list.update(1,6) //將下標1的元素修改成6 list.remove(0) //刪除指定下表的元素 list.insert(0,7) //在指定位置添加元素 list.insertAll(0,List(11,12,13)) //在指定位置插入一個集合 list++=(List(21,22,23)) //在集合後面追加一個集合 list.+=(24) //在集合最後追加一個元素 list.-=(23) //在幾何中刪除指定元素 list--=List(1,2,3) //在集合中刪除一個集合 println(list.size) //返回集合的長度 println(list.length) //返回集合的長度 println(list.sum) //返回集合中全部元素的和 list.foreach(println) }
Set(不可變)
scala.collection.immutable.Set
scala> var s=Set(1,2,3) s: scala.collection.immutable.Set[Int] = Set(1, 2, 3) scala> s += 4 res88: scala.collection.immutable.Set[Int] = Set(1, 2, 3, 4) scala> s++=(List(4,5,6)) res89: scala.collection.immutable.Set[Int] = Set(1, 5, 2, 6, 3, 4) scala> s-=(3) res90: scala.collection.immutable.Set[Int] = Set(1, 5, 2, 6, 4) scala> s.size res91: Int = 5 scala> s.sum res92: Int = 18 scala> s.ma map mapResult max maxBy scala> s.max res93: Int = 6 scala> s.min res94: Int = 1
Set(可變)
scala.collection.mutable.Set
scala> var s=scala.collection.mutable.Set(1,2,3) s: scala.collection.mutable.Set[Int] = Set(1, 2, 3) scala> s += 4 res99: scala.collection.mutable.Set[Int] = Set(1, 2, 3, 4) scala> s++=(List(4,5,6)) res100: scala.collection.mutable.Set[Int] = Set(1, 5, 2, 6, 3, 4) scala> s-=(3) res101: scala.collection.mutable.Set[Int] = Set(1, 5, 2, 6, 4) scala> s.remove(6) res102: Boolean = true scala> s.add(9) res103: Boolean = true scala> s.size res104: Int = 5 scala> s.sum res105: Int = 21 scala> s.min res106: Int = 1 scala> s.max res107: Int = 9
HashMap(不可變)
scala.collection.immutable.HashMap
scala> var hm=HashMap[String,String](("建設","001"),("招商","002")) hm: scala.collection.immutable.HashMap[String,String] = Map(建設 -> 001, 招商 -> 002) scala> hm+="工商"->"003" //等價hm+=("工商","003") scala> hm-=("工商") scala> val value:Option[String] = hm.get("建設") value: Option[String] = Some(001) scala> println(value.getOrElse("")) 001 scala> scala> hm.size res3: Int = 2 scala> for(i<- hm) println(i._1+" -> "+i._2) 建設 -> 001 招商 -> 002 scala> var hm=HashMap[String,String](("建設","001"),("招商","002")) hm: scala.collection.immutable.HashMap[String,String] = Map(建設 -> 001, 招商 -> 002) scala> hm+="工商"->"003" scala> hm-=("工商") scala> //高階函數 scala> var mergerFunction:((String,String),(String,String))=>(String,String)=(tuple1,tuple2)=>{ | println(tuple1+" -> "+tuple2) | if(tuple1._1.equals(tuple2._1)){ | (tuple1._1,tuple2._2) | }else{ | tuple1 | } | } mergerFunction: ((String, String), (String, String)) => (String, String) = $$Lambda$1152/2052572633@742aa00a scala> var newhm=HashMap[String,String](("郵政","004")) newhm: scala.collection.immutable.HashMap[String,String] = Map(郵政 -> 004) scala> var res=hm.merged(newhm)(mergerFunction) res: scala.collection.immutable.HashMap[String,String] = Map(郵政 -> 004, 建設 -> 001, 招商 -> 002) scala> for(i<- res) println(i) (郵政,004) (建設,001) (招商,002)
HashMap(可變)
scala.collection.mutable.HashMap
import scala.collection.mutable object TestHashMap { def main(args: Array[String]): Unit = { var map=mutable.HashMap[String,String](("001","Lijb"),("002","Houy")) map+="003"->"Donghy" //添加一個元組/鍵值對 map-="003" //根據健刪除鍵值對 map.put("004","Gex") //添加一個元組/鍵值對 map.remove("004") //根據健刪除鍵值對 //遍歷方式 map.foreach(t=>println(t._1+"->"+t._2)) map.foreach(println) for(i<- map) println(i) } }
數組集合經常使用方法
排 序
sorted sortBy() sortWith()
def main(args: Array[String]): Unit = { var arr=Array (8,7,3,4,5) arr.sorted.foreach(println)//升序排序 var arr1=Array(("a",1),("c",2),("b",3),("d",5)) arr1.sortBy(t=>t._1) //等價list.sortBy(_._1) arr1.sortWith((t1,t2)=>t1._2<t2._2) arr1.foreach(println) }
數組展開
flatten
def main(args: Array[String]): Unit = { var list=List(Array("a","c"),Array("d","b")) list.flatten.foreach(println) //a,c,d,b }
轉換
map方法將一個函數應用到某個集合的全部元素並返回結果;
foreach將函數應用到每一個元素。
scala> var lst=List("hello world","good good study","day day up") lst: List[String] = List(hello world, good good study, day day up) scala> lst.map(item=>item.split(" ")) res15: List[Array[String]] = List(Array(hello, world), Array(good, good, study), Array(day, day, up)) scala>lst.map(item=>item.split(" ")).flatten res16: List[String] = List(hello, world, good, good, study, day, day, up)
轉換並展開
flatMap
scala> lst.flatMap(item=>item.split(" ")) res19: List[String] = List(hello, world, good, good, study, day, day, up) scala> lst.flatMap(_.split(" ")) res20: List[String] = List(hello, world, good, good, study, day, day, up)
過濾
filter方法輸出全部匹配某個特定條件的元素:
scala> var lst=List("hello world","good good study","day day up") lst: List[String] = List(hello world, good good study, day day up) scala> lst.flatMap(_.split(" ")).filter(_.equals("good")) res22: List[String] = List(good, good)
元組排序
scala> var lst=Array(("a",1),("c",4),("b",3),("d",2)) lst: Array[(String, Int)] = Array((a,1), (c,4), (b,3), (d,2)) scala> lst.sorted res9: Array[(String, Int)] = Array((a,1), (b,3), (c,4), (d,2)) scala> lst.sortBy(x=>x._2) res11: Array[(String, Int)] = Array((a,1), (d,2), (b,3), (c,4))
定製排序規則
scala> var lst=Array(("a",1),("c",4),("b",3),("d",2)) lst: Array[(String, Int)] = Array((a,1), (c,4), (b,3), (d,2)) scala> lst.sortWith((x,y)=> {x._1>y._1} ) res12: Array[(String, Int)] = Array((d,2), (c,4), (b,3), (a,1))
分組統計
scala> var lst=Array("a","b","d","c","b","a","d","d") lst: Array[String] = Array(a, b, d, c, b, a, d, d) scala> lst.groupBy(x=>x) res51: scala.collection.immutable.Map[String,Array[String]] = Map(b -> Array(b, b), d -> Array(d, d, d), a -> Array(a, a), c -> Array(c)) scala> var lst=Array(("a",1),("b",2),("a",2)) lst.groupBy(x=>x._1).map(x=>(x._1,(for(i <- x._2) yield i._2).sum))
fold統計
var lst=Array(Array(1,2,3),Array(1,2,4),Array(5,6,7),Array(9,4)) lst.flatten.fold(0)((x,y)=>x+y)//結果44 全部元素的和
aggregate
scala> var lst=List(1,2,4,5,6) lst: List[Int] = List(1, 2, 4, 5, 6) 初始值 局部計算 彙總邏輯 scala> lst.aggregate(0)((x,y)=>x+y,(x,y)=>x+y) //等價 lst.aggregate(2)(_+_,_+_) res11: Int = 18
Reduce
scala> var lst=List(1,2,4,5,6) lst: List[Int] = List(1, 2, 4, 5, 6) scala> lst.reduce(_+_) res12: Int = 18
group
cala> var lst=Array(1,2,4,5,6,7) lst: Array[Int] = Array(1, 2, 4, 5, 6, 7) scala> lst.grouped(3).toList //將數組按給定長度分紅若干分數組而且組合成一個集合 res18: List[Array[Int]] = List(Array(1, 2, 4), Array(5, 6, 7))
zip
scala> var v=Vector(1,2,4) v: scala.collection.immutable.Vector[Int] = Vector(1, 2, 4) scala> v.zip(Array("a","b","c")) res22: scala.collection.immutable.Vector[(Int, String)] = Vector((1,a), (2,b), (4,c))
unizp
scala> var v=List(("a",1),("b",2),("c",3)) v: List[(String, Int)] = List((a,1), (b,2), (c,3)) scala> v.unzip res24: (List[String], List[Int]) = (List(a, b, c),List(1, 2, 3))
diff | intersect | union
scala> var v=List(1,2,3) v: List[Int] = List(1, 2, 3) scala> v.diff(List(2,3,5)) //diff求並集 res27: List[Int] = List(1) scala> var v=List(1,2,3,5) v: List[Int] = List(1, 2, 3, 5) scala> v.intersect(List(2,4,6)) //intersert求交集 res37: List[Int] = List(2) scala> var v=List(1,2,3,5) v: List[Int] = List(1, 2, 3, 5) scala> v.union(List(2,4,6)) //兩集合進行合併 res0: List[Int] = List(1, 2, 3, 5, 2, 4, 6)
distinct
scala> var v=List(1,2,3,3,5) v: List[Int] = List(1, 2, 3, 3, 5) scala> v.distinct //去重 res29: List[Int] = List(1, 2, 3, 5)
Sliding
scala> var v=List(1,2,3,3,5) v: List[Int] = List(1, 2, 3, 3, 5) scala> v.sliding(2,1).toList //交叉一個元素,每一個集合兩個元素 res35: List[List[Int]] = List(List(1, 2), List(2, 3), List(3, 3), List(3, 5))
數組集合方法的應用場景
字符統計
def main(args: Array[String]): Unit = { var arrs=Array("this is a Statistical demo","good good study","day day up") arrs.map(_.split(" ")) // 按""分割 .flatten //將元素展開 .map((_,1)) //將集合中的元素統計+1 .groupBy(_._1)//按照每個元組的第一個元素(也就是不重複的每個單詞)分類 .map(x=>(x._1,(for(i<- x._2) yield i._2.toInt)//遍歷map,而且遍歷map裏的值算其長度,並提取出來 .toList.sum))//進行相加統計 .toList .sortBy(_._1) .foreach(println) }
文件字符統計
def main(args: Array[String]): Unit = { var source=Source.fromFile("D:\\t_word.txt") var array=ArrayBuffer[String](); val breaks=Breaks breaks.breakable({ val reader = source.bufferedReader() while(true){ val line = reader.readLine() if(line==null){ breaks.break() } array+=line } }) array.flatMap(_.split(" ")) .map((_,1)) .groupBy(_._1) .map(x=>(x._1,x._2.size)) .toList .sortBy(_._1) . foreach(println) }