大數據(九)--Scala編程語言-安裝+基礎

Scala介紹

  Scala是一門多範式的編程語言, 它與Java極其相似, 都是運行在JVM上的編程語言. Scala設計的初衷是將面向對象編程和函數式編程的各種特性集成起來。

  Scala是一門編譯型語言, 同時也是一門弱類型語言, 它的語法簡單, 但是靈活, 極大的靈活性使得初學者對其難以駕馭. 大數據中Spark底層是由Scala語言實現, 因此想要學習Spark, 要先入門Scala.

  Scala程序運行時, 會先編譯成Java字節碼文件, 然後該文件再在JVM上運行.

  Scala六個特徵

  1. 可以和Java無縫混編
  2. 支持類型推測(自動推測類型)
  3. 併發和分佈式(Actor, 類似Java中的Thread)
  4. 結合Java中interface實現和abstract繼承的新特性trait
  5. 模式匹配match(類似Java中的switch, 但支持的類型更加豐富)
  6. 高階函數(面向函數編程)
    - 函數參數是函數
    - 函數返回是函數

Scala安裝

1. windows下安裝, 環境配置

  官網下載scala2.10.4的壓縮文件:https://www.scala-lang.org/download/2.10.4.html
  解壓並配置環境變量(和配置jdk一樣)
  新建SCALA_HOME
在這裏插入圖片描述
  上個步驟完成後,編輯Path變量,在後面追加如下:
  %SCALA_HOME%\bin;
在這裏插入圖片描述
  打開cmd,輸入:scala - version 看是否顯示版本號,確定是否安裝成功
在這裏插入圖片描述
  可通過scala命令進入Scala Shell.

2. Scala-IDEA

  開發Scala程序, 可以去下載一個支持Scala的eclipse. 不推薦在現有的eclipse中安裝Scala插件的形式.
  下載網址:http://scala-ide.org/download/sdk.html

3. IntelliJ IDEA中安裝Scala插件

  打開idea,close項目後,點擊Configure->Plugins
在這裏插入圖片描述
  搜索scala,點擊Install安裝
在這裏插入圖片描述

  或者直接在settings的plugins中搜索scala
在這裏插入圖片描述
  由於筆者之前以及安裝過, 因此提示的是卸載(Uninstall), 如果之前未安裝過會是一個綠色的√, 文字未Install.

4. IntelliJ IDEA-2017.3版本中創建Scala項目

  創建Scala項目
在這裏插入圖片描述
  設置項目名, 存放位置, JDK以及ScalaSDK
在這裏插入圖片描述
  在src下新建包, 右鍵新建Scala Class
在這裏插入圖片描述
  注意這裏選擇Object類型
在這裏插入圖片描述

Scala基礎

1. 數據類型

Scala中與Java相似的數據類型

數據類型 描述 範圍
Byte 8bit有符號數字 -128~127
Short 16bit有符號數字 -32768~32767
Int 32bit有符號數字 -2147483648~2147483647
Long 64bit有符號數字 -263~263-1
Float 32位IEEE754標準的單精度浮點數 1.4E-45~3.4028235E38
Double 64位IEEE754標準的雙精度浮點數 4.9E-324~1.7976931348623157E308
Char 16位Unicode字符 U+0000~U+FFFF
String 字符串 -
Boolean 布爾類型 true/false
Null 空值或空引用, 是AnyRef的子類 -

Scala獨有的數據類型

數據類型 描述
Unit 表示無值, 同void
Any 所有類型的超類, 任何實例都屬於Any. Any的父類還是Object
AnyRef 所有引用類型的超類
AnyVal 所有值類型的超類
Nothing 表示沒有值, 所有其他類型的子類型, 該類型的對象不會報空指針異常
None Option的子類,用於安全的函數返回值
some Option的子類, 用於存儲元組數據(“hello”,“scala”), 類似於map,由一對小括號包括

數據類型類圖
在這裏插入圖片描述

2. 常量&變量

  定義變量: var a: Int = 1
  定義常量: val b: Int = 1
  由於Scala支持類型推測, 因此常量或變量的類型一般省略, 上述定義可簡寫爲:
    var a = 1
    val b = 1
  注意:
    val修飾的常量一旦定義, 則其值不可修改. 如果修改, 會報error: reassignment to val.
    Scala中語句之後可以不加分號’;’ , 自動根據換行符區分. 但是, 如果多條語句在一行, 必須加分號隔開.

3. 類&對象

  創建類

class Person {
	val name = "qibao"
	val age = 22
	def tell() = {
		"My name is " + name + ". My age is " + age
	}
}

   val 聲明兩個常量, 不可修改.
   def 聲明函數, 具體格式之後再說.

  創建對象

object OnePerson {
	def main(args: Array[String]): Unit = {
		val person = new Person()
		println(person.name + ":" + person.tell())
	}
} 

注意:

  1. 命名規則與Java保持一致即可, 包括類首字母都是大寫, 方法首字母小寫, 符合駝峯式命名法.
  2. Scala中class默認可傳參, 默認的傳參數就是默認的構造函數. 重載構造函數時, 必須調用默認構造函數.
//定義類時傳參
class Person(var name: String, var age: Int) {
  var fcp = 0.0
  //重載構造器, 參數不能有修飾符
  def this(name: String, age:Int, facePower:Double) = {
  	//調用默認構造器
    this(name,age)
    fcp = facePower
  }
  def tell() = println(name + ":" + age + ":" + fcp)
}
  1. 在class修飾的類中, 如果參數用var來修飾, 那麼可以之間通過對象名.屬性來調用; 如果參數用val修飾, 則不能通過對象名.屬性來修改; 如果參數沒有修飾符, 則該屬性爲私有, 無法通過對象來調用.
object ScalaBase {
  def main(args: Array[String]): Unit = {
    var p1 = new Person("qb", 22, '男')
    p1.name = "qibao"
//    p1.age = 23   val定義無法修改
    println(p1.age)
//    println(p1.sex) 沒有修飾符,無法訪問
  }
}

class Person(var name: String, val age: Int, sex: Char) {
  def tell() = println(name + ":" + age)
}

  1. Scala中object修飾的單例對象, 不可傳遞參數. 在Scala中沒有static這一關鍵字, 所有靜態屬性和方法都需要放在object修飾的對象中.
  2. 創建object修飾的對象時, 不用new這一關鍵字; 創建class修飾的對象時, 使用new, 且在new的時候, 除了class中的方法不執行外, 其餘的都執行.
  3. 如果在同一個文件中, object對象和class類的名稱相同, 則這個對象就是這個類的伴生對象, 這個類就是這個對象的伴生類. 伴生對象和伴生類之間可以互相訪問私有變量.
  4. Scala中伴生對象+伴生類 = Java中某個類
  5. apply方法的使用: apply相當於Java中的靜態代碼塊, 創建對象時自動調用. 因此它要在object修飾的伴生對象中定義.
class Person(var name: String, var age: Int) {
  def tell() = println(name + ":" + age)
}
object Person {
  def apply(name: String, age: Int) = new Person(name, age)
}

定義之後再創建Person對象時, 可以省略new關鍵字
object ScalaBase {
	def main(args: Array[String]): Unit = {
	   var p1 = new Person("qb", 22)
	   var p2 = Person("qb", 23)
	  }
}

4. 判斷語句

  判斷語句與Java中的用法完全一致, 兩種形式 if-else和if-else if -else.

 val age =18 
 if (age < 18 ){
  	println("no allow")
  }else if (18 <= age && age <= 20){
  	println("allow with other")
  }else{
  	println("allow self")
  }

5.循環語句

5.1 to和until的使用

  這倆個方法都會產生一個集合, 其中until前閉後開, to前後均閉.
  比如說,1 until 10 返回1到9的數組, 1 to 10 返回1到10的數組.

println(1 to 5 )//打印 1, 2, 3, 4, 5
println(1.to(5))//與上面等價,打印 1, 2, 3, 4, 5
   
println(1 until 5 ) //不包含最後一個數,打印 1,2,3,4
println(1.until(5))//與上面等價
    

  除按順序打印外, 還可設置步長.

println(1 to (10 ,2))//步長爲2,從1開始打印 ,1,3,5,7,9
println(1.to(10, 2)) 

println(1 until (10 ,3 ))//步長爲3,從1開始打印,打印1,4,7

5.2 for循環

  1. 通過索引
 var list = List("hello", "scala", "spark")
 for (index <- 0 until list.length) println(list(index))
  1. 增強for循環
for (elem <- list) println(elem)

  在用Scala寫for循環時, 不需要指定循環變量的類型, Scala會自動推測出其類型, 同時給循環變量賦值使用"<-". Scala習慣來講, 如果判斷或循環語句只有一行, 一般把它們放在一行.

  1. 多層for循環
      當有多層循環時, 可用分號’;'將循環變量隔開.
//打印九九乘法表
for (i <- 1 to 9; j <- 1 to 9) {
      if (j <= i) print(i + "*" + j + "=" + i * j + "\t")
      if (j == i) println()
    }
  1. for循環中添加條件判斷
      Scala的for循環, 還可以在循環變量設置的( ) 中添加條件判斷, 只需用分號’;'將它們隔開即可.
for (i <- 1 to 100; if i % 2 == 0) println(i)

for (i <- 1 to 10 ; if i % 2 == 0 ;if i == 4 ) println(i)
  1. yield關鍵詞自動封裝集合
      在for循環中使用yield關鍵詞後, 能夠將符合要求的元素自動封裝到一個集合中.
//將1-20之間的偶數封裝到rest集合中
val rest = for (i <- 1 to 20; if i % 2 == 0) yield i
for (elem <- rest) println(elem)

5.3 while和do…while循環

  Scala中while和do-while循環的用法與Java中也是完全一致的, 唯一一點需要注意的是Scala語法中沒有break語句, 因此想要跳出循環可以自定義一個布爾類型的標識符.

var flag = true
var index = 0
while (flag && index < 50){
   index += 1
   if (index == 20) flag = false
   println(index)
}

  通過上述幾個實例, 可以看出Scala語法的靈活性以及在寫Scala代碼時遵循的一些簡寫習慣, 之後在函數板塊會有更多簡寫規則, 更能體現Scala的靈活.

Scala函數

1. 函數定義

在這裏插入圖片描述

  1. 函數定義用def關鍵詞.
  2. 可有參也可無參,傳參時需指定參數類型.
  3. 函數可寫返回值類型也可不寫,Scala會自動推斷. 但有時候不能省必須寫,比如在遞歸函數或函數的返回值是函數類型的時候.
  4. Scala中函數有返回值時,return可寫可不寫. 如果不寫, 函數會把最後一行當做結果返回。當寫return時,必須要寫函數的返回值.
  5. 如果函數體只有一行,可以將方法名和函數體寫在一行.
  6. 傳遞給方法的參數可以在方法中使用,並且scala規定方法傳過來的參數爲val的,不是var的。
  7. 如果去掉方法體前面的等號,那麼這個方法返回類型必定是Unit的。這種說法無論方法體裏面什麼邏輯都成立,scala可以把任意類型轉換爲Unit.假設,裏面的邏輯最後返回了一個string,那麼這個返回值會被轉換成Unit,並且值會被丟棄。
 def fun1(a: Int, b: Int): Int = {
   return a + b;
 }

 def fun2(a: Int, b: Int) = {
   a + b
 }

 def fun3(a: Int, b: Int) = a + b

  從f1–>f3, 由Java編碼思想向Scala編碼思想的轉變. Scala追求的是一種高效, 快速的編碼習慣.

2. 遞歸函數

  遞歸函數: 函數自身調用自身

//求一個數的階乘
def fun4(n: Int): Int = {
    if (n == 1 || n == 0) 1
    else n * fun4(n - 1)
  }

  需要注意的是, 遞歸函數在最後一次返回之前返回的是一個函數, 此時Scala無法推測其類型. 因此, 在Scala中寫遞歸函數時, 必須明確返回值類型, 否則會報Type mismatch的錯.

3. 帶默認值的函數

  默認值函數: 函數在定義時, 指定某個或多個參數的值.

object ScalaFun1 {
  def main(args: Array[String]): Unit = {
    println(fun5());
    println(fun5(1));
    println(fun5(b=2));
    println(fun5(1,2));
    
    println(fun5_1(1));
    println(fun5_2(b = 2));
  }
  //默認值函數
  def fun5(a: Int = 10, b: Int = 20) = a + b

  def fun5_1(a: Int, b: Int = 20) = a + b

  def fun5_2(a: Int = 10, b: Int) = a + b
}

  注意: 當參數有默認值時, 可以不指定, 也可指定. 但如果想要給不是第一個位置的參數指定值時, 需要明確參數的名稱.

4. 可變參數列表的函數

  可變參數列表函數: 函數參數可以是一個也可以是多個. Java中使用…來指定可變參數列表方法, Scala中使用*.

def fun6(args: Double*) = {
    var sum = 0.0
    for (arg <- args) sum += arg
    sum
  }

  注意: Scala中+=, -=前後的數值類型必須相同, Scala不會隱式類型轉換; +,- 可以前後類型不相同. 此外, 函數最後一行作爲返回值, for的返回是Unit(空), 因此需要另起一行返回sum.
  補充一點: Scala中沒有++, --運算符.

5. 匿名函數

  匿名函數: 沒有名字的函數. 既然沒有名字就無法調用, 因此可以將匿名函數賦給一個變量.

def main(args: Array[String]): Unit = {
    println(fun7(1, 2));
  }
val fun7 = (a: Int, b: Int) => a + b

  注意:

  1. 匿名函數可以有參, 也可無參
  2. 匿名函數的參數和方法體要用 => 來連接
  3. 匿名函數不能顯示的聲明返回值類型

6. 嵌套函數

  匿名函數: 函數體內又定義了一個函數.

def fun8 (n:Int) = {
    def fun9(a:Int,b:Int):Int ={
      if(a==1) b
      else fun9(a-1,a*b)
    }

    fun9(n,1)
  }

7. 偏應用函數

  偏應用函數: 偏應用函數是一種表達式, 它實際是調用了其他函數. 只不過不需要提供函數所需的全部參數, 只需提供部分或不提供參數, 不提供的參數單獨指定.

//普通函數
  def log(date: Date, content: String) = {
    println("date" + date + "\tcontent" + content)
  }

  val date = new Date()
  log(date,"log1")
  log(date,"log2")
  log(date,"log3")
  
  //偏應用函數
  val logBoundDate = log(date,_:String)
  logBoundDate("log1_1")
  logBoundDate("log2_1")
  logBoundDate("log3_1")

  分析代碼可知, 偏應用並不是真實的函數, 而是將某個函數的一個或多個參數指定後的表達式. 上述需求中, 需要傳入時間和內容作爲日誌, 但時間往往是不變的, 變化的只是內容, 因此可以使用偏應用函數來處理. 其中’_’ 代表佔位符, 指代第二個參數.

8. 高階函數

  高階函數: 高階函數是指(1)函數的參數是函數; (2)函數的返回類型是函數; (3)函數的參數和返回類型是函數 的函數.

  函數作爲參數或返回值進行傳遞時, 也是沒有名字的, 因此也相當於是匿名函數, 上文也說過, 匿名函數的參數和方法體之間用 => 進行連接.

  • 函數的參數是函數
def highFun1(f: (Int) => Int, num: Int) = f(num)
def temF1(num: Int) = num + 1

//調用函數
println(highFun1(temF1, 1))

  代碼分析: highFun1有兩個參數, 其中一個參數是函數, 函數的類型匹配參數爲Int, 返回爲Int的函數. 由此可以看出, Scala把函數當成對象在函數之間傳遞, 這也是Scala函數式編程的體現.
  同時在使用高階函數時, 函數參數直接寫函數名, 不需要加括號(), 加括號說明是調用函數, 使用的是函數返回的結果, 直接寫函數名纔是將函數切切實實地作爲參數進行傳遞.

  • 函數的返回類型是函數
def highFun2(num: Int): (Int, Int) => Double = {
    def temF2(num1: Int, num2: Int): Double = {
      num + num1 + num2
    }

    temF2
  }

//調用函數
var fun = highFun2(1)
println(fun(1,1))
//上述兩行代碼也可簡寫爲
println(highFun2(1)(1,1))

  代碼分析: highFun2有一個Int類型的參數, 返回一個(Int, Int) => Double格式的函數, 返回時可以像上述代碼中寫的一樣, 主動定義一個函數, 然後返回, 也可以直接在最後一行寫一個匿名函數返回. 如下:

def highFun3(num: Int): (Int, Int) => Double = {
	(num1: Int, num2: Int) => num1 + num2 + num
}

  調用highFun2時, 它返回一個函數, 這就相當於匿名函數, 可以通過一個變量來接收這個函數, 然後再通過給這個變量設置參數的形式得到返回函數的值.

  • 函數的參數和返回類型都是函數
def highFun4(f: (Int,Int) => Int, num1: Int): (Int) => Double = {
    (num: Int) => num + f(num1,1)
 }

//調用函數
var fun = highFun4((a:Int,b:Int)=>a+b,1)
var res0 = fun(1)
println(res0)
//上述可簡化
var res1 = highFun4((a:Int,b:Int)=>a+b,1)(1)
println(res1)
//如果匿名函數的參數在方法體中只使用了一次 那麼可以寫成_表示    
var res2 = highFun4(_+_,2)(2)
println(res2)

  highFun4的參數類型和返回類型不用再說了, 主要是函數調用時代碼簡化的過程, highFun4的匿名函數參數已經指定該函數有兩個參數(這句話比較拗口, 建議讀者慢慢領會), 在調用highFun4時, 如果這個函數參數的兩個參數只使用一次, 那麼可以用 _ 來表示這些參數.
  Scala學習地越深入, 越容易發現Scala許多操作類似jQuery中的鏈式操作, 而且有些內容能省則省, 這就於人類日常交流類似, 雙方都知道的事情, 就沒有必要每次說話時再點出來.

9. 柯里化函數

  柯里化函數: 柯里化函數實質上是一類高階函數的簡化.
  先來看這一類高階函數:

def highFun5(num: Int): (Int) => Int = {
    def fun(a: Int) = num + a
    fun
}

//調用時,可以將兩個參數寫一行
println(highFun5(1)(2))

  再來看柯里化函數:

def klhFun(a: Int)(b: Int) = a + b

//調用方式跟上述代碼一致
println(klhFun(1)(2))

  滿足上述樣式的函數就是柯里化函數, 再比如:

 def fun7(a :Int,b:Int)(c:Int,d:Int) = {
      a+b+c+d
    }
println(fun7(1,2)(3,4))
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章