您好,登錄后才能下訂單哦!
[TOC]
Scala是一門多范式(multi-paradigm)的編程語言,設計初衷是要集成面向對象編程和函數式編程的各種特性。
Scala運行在Java虛擬機上,并兼容現有的Java程序。
Scala源代碼被編譯成Java字節碼,所以它可以運行于JVM之上,并可以調用現有的Java類庫。
函數編程范式更適合用于Map/Reduce和大數據模型,它摒棄了數據與狀態的計算模型,著眼于函數本身,而非執行的過程的數據和狀態的處理。函數范式邏輯清晰、簡單,非常適合用于處理基于不變數據的批量處理工作,這些工作基本都是通過map和reduce操作轉換數據后,生成新的數據副本,然后再進行處理。
像Spark,Flink等都是采用Scala開發的,所以學習好大數據,掌握scala是必要的。
官網:http://scala-lang.org/
1、安裝JDK
2、JAVA_HOME, PATH
3、Maven
4、SCALA SDK
下載地址:http://scala-lang.org/download/all.html
這里選擇的版本為Scala 2.10.5,分為windows和linux版本
5、配置SCALA_HOME
在windows環境變量中添加SCALA_HOME
6、驗證
scala -version
Scala code runner version 2.10.5 -- Copyright 2002-2013, LAMP/EPFL
object HelloWorld {
def main(args:Array[String]):Unit = {
println("Hello World!")
}
}
保存為HelloWorld.scala
,然后再執行下面兩步即可:
scalac HelloWorld.scala
scala HelloWorld
1.可拓展
面向對象
函數式編程
2.兼容JAVA
類庫調用
互操作
3.語法簡潔
代碼行短
類型推斷
抽象控制
4.靜態類型化
可檢驗
安全重構
5.支持并發控制
強計算能力
自定義其他控制結構
1、都是基于JVM虛擬機運行的
Scala編譯之后的文件也是.class,都要轉換為字節碼,然后運行在JVM虛擬機之上。
2、Scala和Java相互調用
在Scala中可以直接調用Java的代碼,同時在Java中也可以直接調用Scala的代碼
3、Java8 VS Scala
1)Java8(lambda)沒有出來之前,Java只是面向對象的一門語言,但是Java8出來以后,Java就是一個面向對象和面向函數的混合語言了。
2)首先我們要對Scala進行精確定位,從某種程度上講,Scala并不是一個純粹的面向函數的編程語言,有人認為Scala是一個帶有閉包的靜態面向對象語言),更準確地說,Scala是面向函數與面向對象的混合。
3)Scala設計的初衷是面向函數FP,而Java起家是面向對象OO,現在兩者都是OO和FP的混合語言,是否可以這么認為:Scala= FP + OO,而Java =OO + FP?
由于面向對象OO和面向函數FP兩種范式是類似橫坐標和縱坐標的兩者不同坐標方向的思考方式,類似數據庫和對象之間的不匹配阻抗關系,兩者如果結合得不好恐怕就不會產生1+1>2的效果。
面向對象是最接近人類思維的方式,而面向函數是最接近計算機的思維方式。如果你想讓計算機為人的業務建模服務,那么以OO為主;如果你希望讓計算機能自己通過算法從大數據中自動建模,那么以FP為主。所以,Java可能還會在企業工程類軟件中占主要市場,而Scala則會在科學計算大數據分析等領域搶占Java市場,比如Scala的Spark大有替代Java的Hadoop之趨勢。
1、Scala解釋器讀到一個表達式,對它進行求值,將它打印出來,接著再繼續讀下一個表達式。這個過程被稱做讀取--求值--打印--循環,即:REPL。
從技術上講,scala程序并不是一個解釋器。實際發生的是,你輸入的內容被快速地編譯成字節碼,然后這段字節碼交由Java虛擬機執行。正因為如此,大多數scala程序員更傾向于將它稱做“REPL”
2、scala
scala>"Hello"
res1: String = Hello
scala> 1+2
res5: Int = 3
scala>"Hello".filter(line=>(line!='l'))
res2: String = Heo
你應該注意到了在我們輸入解釋器的每個語句后,它會輸出一行信息,類似res0:
java.lang.String
= Hello。輸出的第一部分是REPL給表達式起的變量名。在這幾個例子里,REPL為每個表達式定義了一個新變量(res0到res3)。輸出的第二部分(:后面的部分)是表達式的靜態類型。第一個例子的類型是java.lang.String,最后一個例子的類型則是scala.util.matching.Regex。輸出的最后一部分是表達式求值后的結果的字符串化顯示。一般是對結果調用toString方法得到的輸出,JVM給所有的類都定義了toString方法。
1、Scala中沒有static的類,但是他有一種類似的伴生對象
2、字段:
字段/變量的定義Scala中使用 var/val 變量/不變量名稱 : 類型的方式進行定義,例如:
var index1 : Int = 1
val index2 : Int = 1
其中var與val的區別在于,var是變量,以后的值還可以改變,val的值只能在聲明的時候賦值,但是val不是常量,只能說是不變量或只讀變量。
3、大家肯定會覺得這種var/val名稱 : 類型的聲明方式太過于繁瑣了,有另外一種方式
所以你在聲明字段的時候,可以使用編譯器自動推斷類型,即不用寫: 類型
,例如:
var index1 = 1 (類型推斷)
val index2 = 1
這個例子演示了被稱為類型推斷(type inference)的能力,它能讓scala自動理解你省略了的類型。這里,你用int字面量初始化index1,因此scala推斷index2的類型是Int。對于可以由Scala解釋器(或編譯器)自動推斷類型的情況,就沒有必要非得寫出類型標注不可。
4、a.方法(b)
這里的方法是一個帶有2個參數的方法(一個顯示的和一個隱式的)
1.to(10), 1 to 10, 1 until 10
scala> 1.to(10)
res19: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
scala> 1 to 10
res20: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
scala> 1 until 10
res21: scala.collection.immutable.Range = Range(1, 2, 3, 4, 5, 6, 7, 8, 9)
5、其實根據函數式編程思想中,var變量是個不好的存在,Scala中推薦大家盡可能的采用val的不變量,主要原因是
? scala擁有和java一樣的數據類型,和java的數據類型的內存布局完全一致,精度也完全一致。
下面表格中是scala支持的數據類型:
上表中列出的數據類型都是對象,也就是說scala沒有java中的原生類型。也就是說scala沒有基本數據類型與包裝類型的概念。
Scala里,你可以舍棄方法調用的空括號。例外就是如果方法帶有副作用就加上括號,如println(),不過如果方法沒有副作用就可以去掉括號,如String上調用的toLowerCase:
scala> "Hello".toLowerCase
res22: String = hello
scala> "Hello".toLowerCase()
res23: String = hello
Scala里,你可以舍棄方法調用的空括號。例外就是如果方法帶有副作用就加上括號,如println(),不過如果方法沒有副作用就可以去掉括號,如String上調用的toLowerCase:
你可以通過中綴操作符,加號(+),減號(-),乘號(*),除號(/)和余數(%),在任何數類型上調用數學方法, scala中的基礎運算與java一致
scala> 1.2 + 2.3?
res6: Double = 3.5?
scala> 3 - 1?
res7: Int = 2?
scala> 'b' - 'a'?
res8: Int = 1?
scala> 2L * 3L?
res9: Long = 6?
scala> 11 / 4?
res10: Int = 2?
scala> 11 % 4?
res11: Int = 3?
scala> 11.0f / 4.0f?
res12: Float = 2.75?
scala> 11.0 % 4.0?
res13: Double = 3.0
你可以用關系方法:大于(>),小于(<),大于等于(>=)和小于等于(<=)比較數類型,像等號操作符那樣,產生一個Boolean結果。另外,你可以使用一元操作符!(unary_!方法)改變Boolean值
scala> 1 > 2
res16: Boolean = false
scala> 1 < 2
res17: Boolean = true
scala> 1.0 <= 1.0
res18: Boolean = true
scala> 3.5f >= 3.6f
res19: Boolean = false
scala> 'a' >= 'A'
res20: Boolean = true
scala> val thisIsBoring = !true
thisIsBoring: Boolean = false
scala> !thisIsBoring
res21: Boolean = true
scala> ("he" + "llo") == "hello"
res33: Boolean = true
=== 比較2個不同的對象
scala> 1 == 1.0
res34: Boolean = true
其比較的是值,而不是Java概念中的地址值。另外第二個例子,1 == 1.0,會為true,是因為1會做類型的提升變為1.0
1、Scala的if/else語法結構和Java或者C++一樣,不過,在Scala中if/else表達式有值,這個值就是跟在if或else之后的表達式的值
val x = 3
if(x > 0) 1 else -1
上述表達式的值是1或-1,具體是哪一個取決于x的值,同時你也可以將if/else表達式的值復制給變量
val s = if(x>0) 1 else -1 // 這與如下語句的效果是一樣的
if(x >0) s = 1 else s =-1
不過,第一種寫法更好,因為它可以用來初始化一個val,而在第二種寫法當中,s必須是var
2、val result = if(personAge > 18) "Adult" else 0
其中一個分支是java.lang.string,另外一個類型是Int, 所以他們的公共超類是Any
3、如果else丟失了
if(x>0) 1
那么有可能if語句沒有輸出值,但是在Scala中,每個表達式都有值,這個問題的解決方案是引入一個Unit類,寫作(),不帶else語句的if語句等同于if(x>0) 1else ()
1、在Java和C++中,每個語句都已分號結束。但是在Scala中—與JavaScript和其它語言類似—行尾的位置不需要分號。同樣,在}、else以及類似的位置也不需要寫分號。
不過,如果你想在單行中寫下多個語句,就需要將他們以分號隔開
If(n>0){r = r *n ; n-=1}
我們需要用分號將r = r *n 和n -=1隔開,由于有},在第二個語句之后并不需要寫分號。
2、分行顯示
If(n>0){
r = r *n
n-=1
}
scala> val n = 9
n: Int = 9
scala> var f = 0
f: Int = 0
scala> var m = 5
m: Int = 5
scala> val d = if(n < 18){f = f + n ; m = m + n ; f + m}
d: AnyVal = 23
如果要打印一個值,我們用print或println函數。后者在打印完內容后會追加一個換行符。舉例來說,
print("Answer:")
println(42)
與下面的代碼輸出的內容相同:
println("Answer:" + 42)
另外,還有一個帶有C風格格式化字符串的printf函數:system.in
printf("Hello,%s! You are %d years old.\n", "Fred", 42)
你可以用readLine函數從控制臺讀取一行輸入。如果要讀取數字、Boolean或者是字符,可以用readInt、readDouble、readByte、readShort、readLong、readFloat、readBoolean或者readChar。與其他方法不同,readLine帶一個參數作為提示字符串:
scala> val name = readLine("Please input your name:")
Please input your name:name: String = xpleaf
一個簡單的輸入輸出案例如下:
val name = readLine("What's Your name: ")
print("Your age: ")
val age = readInt()
if(age > 18) {
printf("Hello, %s! Next year, your will be %d.\n", name, age + 1)
}else{
printf("Hello, %s! You are still a children your will be %d.\n", name, age + 1 +" Come up!!!")
}
}
Scala擁有與Java和C++相同的while和do循環。例如:
object _04LoopDemo {
def main(args: Array[String]):Unit = {
var sum = 0
var i = 1
while(i < 11) {
sum += i
i += 1
}
}
}
Scala 也有do-while循環,它和while循環類似,只是檢查條件是否滿足在循環體執行之后檢查。例如:
object _04LoopDemo {
def main(args: Array[String]):Unit = {
var sum = 0
var i = 1
do {
sum += i
i += 1
} while(i <= 10)
println("1+...+10=" + sum)
}
}
登錄用戶名密碼的游戲:三次機會,從控制臺輸入輸入用戶名密碼,如果成功登錄,返回登錄成功,失敗,則反饋錯誤信息!如下:
object _05LoopTest {
def main(args: Array[String]):Unit = {
val dbUser = "zhangyl"
val dbPassword = "uplooking"
var count = 3
while(count > 0) {
val name = readLine("親,請輸入用戶名:")
val pwd = readLine("請輸入密碼:")
if(name == dbUser && pwd == dbPassword) {
println("登陸成功,正在為您跳轉到主頁吶," + name + "^_^")
// count = 0
return
} else {
count -= 1
println("連用戶名和密碼都記不住,你一天到底在弄啥嘞!您還有<" + count + ">次機會")
}
}
}
}
Scala沒有與for(初始化變量;檢查變量是否滿足某條件;更新變量)
循環直接對應的結構。如果你需要這樣的循環,有兩個選擇:一是使用while循環,二是使用如下for語句:
for (i <- 表達式)
讓變量i
遍歷< -
右邊的表達式的所有值。至于這個遍歷具體如何執行,則取決于表達式的類型。對于Scala集合比如Range而言,這個循環會讓i依次取得區間中的每個值。
遍歷字符串或數組時,你通常需要使用從0到n-1的區間。這個時候你可以用until方法而不是to方法。util方法返回一個并不包含上限的區間。
val s = "Hello"
var sum = 0
for (i <- 0 until s.length) { // i的最后一個取值是s.length - 1
sum += s(i) // 注意此時為對應的ASCII值相加
}
在本例中,事實上我們并不需要使用下標。你可以直接遍歷對應的字符序列:
var sum = 0
for (ch <- "Hello") sum += ch
說明:Scala并沒有提供break或continue語句來退出循環。那么如果需要break時我們該怎么做呢?有如下幾個選項:
object _06LoopBreakTest {
def main(args: Array[String]):Unit = {
import scala.util.control.Breaks._
var n = 15
breakable {
for(c <- "Spark Scala Storm") {
if(n == 10) {
println()
break
} else {
print(c)
}
n -= 1
}
}
}
}
1、除了for循環的基本形態之外,Scala也提供了其它豐富的高級特性。比如可以在for循環括號里同時包含多組變量 <- 表達式
結構,組之間用分號分隔
for (i <- 1 to 3;j <- 1 to 3) print ((10 * i +j) + " ")
for循環的這種結構類似Java中的嵌套循環結構。
例如要實現一個九九乘法表,使用基本的for循環形態時,代碼如下:
object _07LoopForTest {
def main(args: Array[String]):Unit = {
for(i <- 1 to 9) {
for(j <- 1 to i){
var ret = i * j
print(s"$i*$j=$ret\t")
}
// println()
System.out.println()
}
}
}
而使用高級for循環時,如下:
object _07LoopForTest {
def main(args: Array[String]):Unit = {
// for(i <- 1 to 9; j <- 1 to 9 if j <= i) {
for(i <- 1 to 9; j <- 1 to i) {
var ret = i * j
print(s"$i*$j=$ret\t")
if(i == j) {
println
}
}
}
}
2、if循環守衛
可以為嵌套循環通過if表達式添加條件:
for (i <- 1 to 3; j <- 1 to 3 if i != j) print ((10 * i + j) + " ")
if表達式是否添加括號,結果無變化:
for (i <- 1 to 3; j <- 1 to 3 if (i != j)) print ((10 * i + j) + " ")
注意:注意在if之前并沒有分號。
3、For推導式
Scala中的yield不像Ruby里的yield,Ruby里的yield象是個占位符。Scala中的yield的主要作用是記住每次迭代中的有關值,并逐一存入到一個數組中。用法如下:
for {子句} yield {變量或表達式}
1)如果for循環的循環體以yield開始,則該循環會構造出一個集合,每次迭代生成集中的一個值:
scala> for(i <- 1 to 10) yield println(i % 3)
1
2
0
1
2
0
1
2
0
1
res47: scala.collection.immutable.IndexedSeq[Unit] = Vector((), (), (), (), (), (), (), (), (), ())
scala> val ret1 = for(i <- 1 to 10) yield(i)
ret1: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
scala> for(i <- ret1) println(i)
1
2
3
4
5
6
7
8
9
10
2) for 循環中的 yield 會把當前的元素記下來,保存在集合中,循環結束后將返回該集合
Scala中for循環是有返回值的。如果被循環的是Map,返回的就是Map,被循環的是List,返回的就是List,以此類推。
yield關鍵字的簡短總結:
1.針對每一次for循環的迭代, yield會產生一個值,被循環記錄下來(內部實現上,像是一個緩沖區)
2.當循環結束后, 會返回所有yield的值組成的集合
3.返回集合的類型與被遍歷的集合類型是一致的
Scala的異常處理和其它語言比如Java類似,一個方法可以通過拋出異常的方法而不返回值的方式終止相關代碼的運行。調用函數可以捕獲這個異常作出相應的處理或者直接退出,在這種情況下,異常會傳遞給調用函數的調用者,依次向上傳遞,直到有方法處理這個異常。
object _01ExceptionDemo {
def main(args:Array[String]):Unit = {
import scala.io.Source
import java.io.FileNotFoundException
try {
val line = Source.fromFile("./wordcount.txt").mkString
val ret = 1 / 0
println(line)
} catch {
case fNFE:FileNotFoundException => {
println("FileNotFoundException:文件找不到了,傳的路徑有誤。。。")
}
case e:Exception => {
println("Exception: " + e.getMessage)
}
case _ => println("default處理方式")
} finally {
println("this is 必須要執行的語句")
}
}
}
Scala除了方法外還支持函數。方法對對象進行操作,函數不是。要定義函數,你需要給出函數的名稱、參數和函數體,就像這樣:
下面是需要注意的問題:
def myFirstFunction(name:String, age:Int) : String ={
println("name=> " + name +"\t age=> " + age)
"Welcome " + name + " to our world, your age is => " + age
}
在本例中我們并不需要用到return。我們也可以像Java或C++那樣使用return,來立即從某個函數中退出,不過在Scala中這種做法并不常見。
提示:雖然在帶名函數中使用return并沒有什么不對(除了浪費7次按鍵動作外),我們最好適應沒有return的日子。很快,你就會使用大量匿名函數,這些函數中return并不返回值給調用者。它跳出到包含它的帶名函數中。我們可以把return當做是函數版的break語句,僅在需要時使用。
def myFirstFunctionWithoutFeedBackValues: Unit ={
println("This is our first function without feedback values")
}
def printMsg(name:String) = println("Hello, " + name +", welcome happy day!!!")
上面所對應函數的案例如下:
/**
注意:
1.scala中如果要給一個函數做返回值,可以不用return語句,
使用也是可以的,但是scala語言強烈建議大家不要用
因為scala崇尚的是簡約而不簡單
一般也就是將這個return當做break來使用
2.在scala中在同一條語句中,變量名不能和引用函數名重復
3.使用遞歸時,需要指定返回值類型
*/
object _02FunctionDemo {
def main(args:Array[String]):Unit = {
// val ret = myFirstFunc("xpleaf", 23)
// println(ret)
// show("xpleaf", 23)
// val ret = singleLineFunc("xpleaf")
// println(ret)
val ret = factorial(5)
println(ret)
}
def testFunc(num:Int) = {
if(num == 1) {
1
}
10
}
// 使用遞歸來求解5的階乘
def factorial(num:Int):Int = {
if(num == 1) {
1
} else {
num * factorial(num - 1)
}
// 如果num * factorial(num - 1)不寫在else里面,則需要return,否則會有異常,
// testFunc中則不會這樣,所以猜測遞歸函數才會有此問題
}
// 單行函數
def singleLineFunc(name:String) = "hello, " + name // 有"="號,有返回值,自動判斷返回值類型
// def singleLineFunc(name:String) {"hello, " + name} // 沒有“=”號,沒有返回值
// def singleLineFunc(name:String):Unit = "hello, " + name // 有"="號,但指定返回值為空,相當于沒有返回值
// def singleLineFunc(name:String) = println("hello, " + name) // 沒有返回值,println并不是一個表達式
// 沒有返回值的函數
def show(name:String, age:Int):Unit = {
println(s"My name is $name, and I'm $age years old.")
}
// 有返回值的函數
def myFirstFunc(name:String, age:Int):String = {
println(s"My name is $name, and I'm $age years old.")
// return "Welcome you " + name + ", make yourself..."
"Welcome you " + name + ", make yourself..."
}
}
1、我們在調用某些函數時并不顯式地給出所有參數值,對于這些函數我們可以使用默認參數。
def sayDefaultFunc(name: String, address: String = "Beijing", tellphone: String ="139****") ={
println(name +"address=> " + address +"\t tellphone=> " + tellphone)
}
2、不指定具體參數時:給出默認值
sayDefaultFunc("Garry")
3、如果相對參數的數量,你給出的值不夠,默認參數會從后往前逐個應用進來。
sayDefaultFunc("Garry","Shanhai")
4、給出全部的參數值
sayDefaultFunc("Garry","Shanhai","13709872335")
5、帶名參數可以讓函數更加可讀。它們對于那些有很多默認參數的函數來說也很有用。
sayDefaultFunc(address ="上海", tellphone="12109876543",name="Tom")
6、你可以混用未命名參數和帶名參數,只要那些未命名的參數是排在前面的即可:
sayDefaultFunc("Tom",tellphone="12109876543",address= "上海")
前面我們介紹的函數的參數是固定的,本篇介紹Scala函數支持的可變參數列表,命名參數和參數缺省值定義。
重復參數
Scala在定義函數時允許指定最后一個參數可以重復(變長參數),從而允許函數調用者使用變長參數列表來調用該函數,Scala中使用“*”來指明該參數為重復參數。例如
Scala:
def echo(args: String*) = {
for (arg <- args) println(arg)
}
Java:
public staticvoid echo(String ...args){
for(String str: args){
System.out.println(str)
}
}
1、在函數內部,變長參數的類型,實際為一數組,比如上例的String* 類型實際為 Array[String]。
然而,如今你試圖直接傳入一個數組類型的參數給這個參數,編譯器會報錯:
val arr= Array("Spark","Scala","AKKA")
Error message as bellows:
error: type mismatch;
2、為了避免這種情況,你可以通過在變量后面添加_*來解決,這個符號告訴Scala編譯器在傳遞參數時逐個傳入數組的每個元素,而不是數組整體
val arr= Array("Spark","Scala","AKKA")
echo(arr:_*)
一個例子如下:
object _04FunctionDemo {
def main(args:Array[String]):Unit = {
show("Spark", "Strom", "Hadoop")
var arr = Array("Spark", "Strom", "Hadoop")
show(arr:_*)
}
def show(strs:String*) {
for(str <- strs) {
println(str)
}
}
}
1、Scala對于不返回值的函數有特殊的表示法。如果函數體包含在花括號當中但沒有前面的=號,那么返回類型就是Unit。這樣的函數被稱做過程(procedure)。過程不返回值,我們調用它僅僅是為了它的副作用。
案例:
如:我們需要打印一些圖案,那么可以定義一個過程:
def draw(str:String) {
println("-------")
println("|"+" "+"|")
println("|"+ str +"|")
println("|"+" "+"|")
println("-------")
}
2、我們也可以顯示指定的函數的返回值:Unit
1、當val被聲明為lazy時,它的初始化將被推遲,直到我們首次對它取值。例如,
lazy val lines= scala.io.Source.fromFile("D:/test/scala/wordcount.txt").mkString
2、如果程序從不訪問lines ,那么文件也不會被打開。但故意拼錯文件名。在初始化語句被執行的時候并不會報錯。不過,一旦你訪問words,就將會得到一個錯誤提示:文件未找到。
3、懶值對于開銷較大的初始化語句而言十分有用。它們還可以應對其他初始化問題,比如循環依賴。更重要的是,它們是開發懶數據結構的基礎。(spark 底層嚴重依賴這些lazy)
4、加載(使用它的時候才會被加載)
println(lines)
一個例子如下:
object _05LazyDemo {
def main(args:Array[String]):Unit = {
import scala.io.Source
import java.io.FileNotFoundException
try {
lazy val line = Source.fromFile("./wordcountq.txt").mkString // wordcountq.txt這個文件并不存在
// println(line)
} catch {
case fNFE:FileNotFoundException => {
println("FileNotFoundException:文件找不到了,傳的路徑有誤。。。")
}
case e:Exception => {
println("Exception: " + e.getMessage)
}
} finally {
println("this is 必須要執行的語句")
}
}
}
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。