控制結構和函式
在Scala中 ,幾乎所有語法結構都有值
If else
s為什麼有值?
If 語句的返回型別取決於最後一條語句。
語句後面的分號不是必須的
Scala 沒有三元運算子,不需要,if else替代了三元運算子
If 表示式會有一個返回值型別,如果 if 或者 else 返回的型別不一樣,就
返回 Any 型別(所有型別的公共超型別)
如果缺少一個判斷,什麼都沒有返回,但是 Scala 認為任何表示式都會
有值,對於空值,使用 Unit 類,寫做()【叫做無有用佔位符,相當於 java中的 void】
注 :行尾的位置不需要分號,只要能夠從上下文判斷出語句的終止即可。但是如果在單行中寫多個語句,則需要分號分割 。在Scala 中, {}塊,其結果也是一個表示式。塊中最後一個表示式的值就是塊的值。
While表示式
Scala 提供和 Java 一樣的 while 和 do 迴圈,與 If 語句不同,While 語句
本身沒有值,即整個 While 語句的結果是 Unit 型別的()。
while (n > 0) {
r = r * n
n -= 1
println (r)
}
do{
r = r * n
n -= 1
println (r)
} while(n > 0)
注: scala並沒有提供break和contine語句來退出迴圈,如果退出使用
1.使用Boolean型的控制變數
2. 使用巢狀函式,從函式中return
3. 使用Break物件的break方法
import scala.util.control.Breaks._ //手動引入
object Break {
def main(args: Array[String]): Unit = {
var r = 1
var n = 10
breakable{ //傳遞值
while(n > 0){
r = r *n
n -= 1
println(n)
if (n == 5) break //
}
}
}
}
for表示式
Scala 也為 for 迴圈這一常見的控制結構提供了非常多的特性,這些 for
迴圈的特性被稱為 for 推導式(for comprehension)或 for 表示式(for
expression)
推導式一詞起源於函數語言程式設計:
for (i <- -1 to 3; j <- 1 to 3) println(i+" "+j+" ")
像變數名 <- 集合 這樣的表示式被稱為生成器表示式,會基於集合生成單獨的數值
保護式:也叫守衛,可以新增一個或者多個守衛,不需要continue語句
for (i <- 1 to 3;j<- 1 to 3 if i != j)println(i+" "+j)
for推導式可以引入變數
for (i <- 1 to 3; from = 4 -i)println( from +" ")
需要返回值怎麼辦?
使用yield關鍵字在for表示式中生成新的集合,for-yield表示式所生成的集合型別將根據所遍歷的集合型別推導而出
val result = for( i <- 1 to 10)yield i %3
println(result)
{}和()對於 for 表示式來說都可以,for 推導式有一個不成文的約定:當
for 推導式僅包含單一表達式時使用原括號,當其包含多個表示式時使用大括號。值得注意的是,使用原括號時,早前版本的 Scala 要求表示式之間必須使用分號
函式
java中一般通過靜態方法模擬
函式的定義
def 函式名(引數名 : 型別)[: 返回型別] = 函式體
def abs(x : Double) = if(x >= 0) x else -x
遞迴函式
def res(n:Int) : Int = if(n <= 0) 1 else n*res(n -1 )
遞迴函式必須有返回值,且指定函式返回值型別
函式引數預設值
引數型別後面設定預設值
def decotate(str:String,left: String ="[",right: String = "]") = left + str + right
//未設定引數,預設[]
println(decotate("hello"))
//設定引數
println(decotate("hello","<<<",">>>"))
函式的命名引數
不按照原先函式的引數順序,指定引數順序
println(decotate(left = "<<<",right = ">>>",str = "你好"))
變長函式
def sum(args : Int*): Int ={
var result = 0
for (arg <- args) result += arg
result
}
println(sum(1,4,5,6))
需要指定返回型別,否則結果會被捨棄
*_告訴編譯器 1 to 5 當做引數序列處理 Range
println(sum(3 to 6 : _*))
head 和 tail
head是第一個元素,tail是剩下的元素的集合
def reds(qarg: Int*) : Int ={
if (qarg.length == 0) 0
else qarg.head + reds(qarg.tail : _*)
}
println(reds(4,2,4,59,76))
注:
- Scala可以通過=右邊表示式,推斷出函式的返回型別,如果函式體需要多個表示式,可以用程式碼塊{}
2.可以將return當做函式版本的break語句- 遞迴函式一定要指定返回型別
- 變長引數通過*來指定,所有引數會轉換為一個序列
- _* 告訴編譯器,Range當做引數序列化處理
- Head是獲取首元素,tail是獲取剩下元素的序列
過程:
沒有返回值的函式:返回型別是unit,沒有 = 號
def box(s : String) { // Look carefully: no =
val border = " "- - " * s.length + " " --\ \n n" "
println(" "\ \n n " + border + "|" + s + "|\ \n n " + border)
}
懶值
當val被宣告為lazy時,初始化將會被延遲,直到我們對此首次取值
使用事項:
- 用於初始化開銷比較大的語句
- 解決迴圈依賴問題 A依賴B B依賴A
- 是開發懶資料結構的基礎
def lazyed() {
lazy val property = init();//沒有使用lazy修飾
println("after init()")
println(property)
}
異常
當碰到異常情況時,方法丟擲一個異常,終止方法本身執行,異常傳遞給其呼叫者,呼叫者可以處理該異常,也可以升級到它的呼叫者,執行系統會一直這樣升級異常,直到呼叫者處理,如果沒有處理,終止程式
Scala的異常工作機制和java一樣,但是Scala沒有受檢異常,不需要宣告函式或者方法可能丟擲某種異常,受檢異常在編譯器被檢查,java必須宣告方法所會丟擲的異常型別
丟擲異常: 用throw關鍵字,丟擲一個異常物件,所有的異常都是Trowable的子型別,throw表示式都是有型別的,為Nothing,因為Nothing是所有型別的子型別,所以throw表示式可以用在需要型別的地方
//並不像 Java 程式碼那樣,需要宣告方法會丟擲異常,這給程式設計師省去理論很多煩惱。方法上也不需要宣告
def divide(x: Int, y: Int): Int = {
if (y == 0) throw new Exception("Divide by zero")
else x / y
}
捕捉異常
在 Scala 裡,借用了模式匹配的思想來做異常的匹配,因此,在 catch 的
程式碼裡,是一系列 case 字句。
異常捕捉的機制與其他語言中一樣,如果有異常發生,catch 字句是按次
序捕捉的。因此,在 catch 字句中,越具體的異常越要靠前,越普遍的異常越靠後。 如果丟擲的異常不在 catch 字句中,該異常則無法處理,會被升級到呼叫者處。
異常捕獲通常採用模式匹配的語法:
try {
process(in)
} catch {
case ex: IOException => println(ex)
} finally {
in.close()
}
finally 字句用於執行不管是正常處理還是有異常發生時都需要執行的步
驟,一般用於物件的清理工作。
注:
- Throw的型別為Nothing,存在exception的if語句型別返回型別
異常程式碼練習
def root(x : Double) = if (x>0){sqrt(x)}else throw new IllegalArgumentException(" s" +
" should not be negative ")
try{
println(root(4))
println(root(-5))
}catch {
case e: Exception => println(e)
}finally {
println(" finally .... ")
}