scala之控制結構和函式
條件表示式
Scala的if/else語法結構和Java或C++一樣。不過,在Scala中if/else表示式有值,這些值就是跟在if或者else之後的表示式的值。如:if (x >0) 1 else -1,此表示式的值是1或者-1。
scala> var x = 1
x: Int = 1
scala> val s = if(x>0) 1 else -1
s: Int = 1
注:val-定義不可變變數,var-定義可變變數。if/else表示式的值可賦值給變數。
在Scala中,每個表示式都有一個型別。如上例if(x>0) 1 else -1的型別為Int,因為兩個分支的型別都是Int。如果為混合型的表達
式,則型別是兩個分支型別的公共超型別Any。
scala> var x = 1
x: Int = 1
scala> val res = if(x>0) "postive" else -1
res: Any = postive
在Scala中,每個表示式都應該有某種值,如果if分支缺失else部分,如if(x>0) 1,當x<0時,則返回()。
scala> var x = -1
scala> val res = if(x>0) 1 x: Int = -1
res: AnyVal = ()
注:在這種情況下,Scala引入了Unit類,寫做()。不帶else的這個if語句等同於if(x>0) 1 else ()。你可以把()當做是表示“無有用值”的佔位符,將Unit當做在Java或C++中的void。從技術上講,void沒有值但是Unit有一個表示“無值”的值。語句終止
在Java和C++中,每個語句都以分號結束。而在Scala中-與JavaScript和其它指令碼語言類似-行尾的位置不需要分號。
同樣,在}、else以及類似的位置也不必寫分號,只要能夠從上下文明確的判斷出這裡是語句的終止即可。
語句終止注意點:
1.在單行中寫下多個語句,需要將語句以分號分開,最後一條語句可不寫分號。
2.如果語句較長需分多行來寫,需確保第一行以一個不能用做語句結尾的符號結尾。
塊表示式和賦值
在Java和C++中,塊語句是一個包含於{}中的語句序列。每當你需要在邏輯分支或迴圈中放置多個動作時,你都可以使用塊語句。而在 Scala中,{}塊包含一系列表示式,其結果也是一個表示式。塊中最後一個表示式的值就是塊的值。如scala> val res = {val res1 = 6 - 5; val res2 = 3 + 2; res1 + res2}
res: Int = 6
注:{}塊的值取決於最後一個表示式。
在Scala中,賦值動作本身是沒有值的-或者,更嚴格地說,它們的值是Unit型別的。Unit型別等同於Java和C++中的void,而這個
型別只有一個值,寫做()。由於賦值語句的值是Unit類的,別把賦值語句串聯在一起,如賦值語句y=1的值是()。對於Java和C++,
賦值語句的值是被賦的那個值,所以在這些語言中,將賦值語句串聯起來是有意義的。
輸入與輸出
在Scala中,每個原始檔隱含的加入以下引用: 1.import java.lang._ 2.import scala._ 3.import Predef._ 包scala中的Predef物件包含了許多有用的方法-詳情可見Scala API。如,通常我們所使用的println、readLine、assert等。迴圈
Scala擁有與Java和C++相同的while和都迴圈。例如,
scala> var x = 100;
x: Int = 100
scala> var res = 0;
res: Int = 0
scala> while(x>=0) {res = res + x; x = x - 1;}
scala> print(res)
5050
scala> var x = 100;
x: Int = 100
scala> var res = 0;
res: Int = 0
scala> do{res = res + x;x = x -1;}while(x>0)
scala> print(res)
5050
在Scala中,沒有與for(初始化變數;檢查變數是否滿足條件;更新變數)迴圈直接對應的結構,如果你需要使用這樣的迴圈,可使用如下for語句:
scala> var x = 100;
x: Int = 100
scala> var res = 0;
res: Int = 0
scala> for(i <- 1 to x) res = res + i;
scala> println(res)
5050
注:for迴圈的語句結構為-for(i <- 表示式),即讓變數i遍歷<-右邊的表示式的所有值。在此for迴圈中,變數i並沒有進行val或var的指定,該變數的型別是集合的元素型別,迴圈變數的作用域一直持續到迴圈結束。
在Scala中,沒有提供break或continue語句來退出迴圈。那麼如果需要break時,你可以用以下方式去實現:
1.使用Boolean型的控制變數。
2.使用巢狀函式-你可以從函式當中return。
3.使用Breaks物件中的break方法;
import scala.util.control.Breaks._
breakable{
for(...){
if(...) break;//退出breakable塊
}
}
注:此程式碼,控制權的轉移是通過丟擲和捕獲異常完成的,因此,如果時間很重要的話,應儘量避免使用這套機制。
高階for迴圈和for推導式
在Scala中,你可以以變數 <-表示式的形式提供多個生成器,用分號將它們隔開,如:scala> for(i <- 1 to 3; j <- 1 to 3) print((10 * i + j) + " ")
11 12 13 21 22 23 31 32 33
每個生成器可以帶一個守衛,以if開頭的Boolean表示式,如:scala> for(i <- 1 to 3; j <- 1 to 3 if i != j) print((10 * i + j) + " ")
12 13 21 23 31 32
注:if之前沒有分號。
你可以使用任意多的定義,引入可以在迴圈中使用的變數,如
scala> for(i <- 1 to 3; from = 4 - i; j <- from to 3) print((10 * i + j) + " ")
13 22 23 31 32 33
在Scala中,如果迴圈體以yield開始,則該迴圈會構造出一個集合,每次迭代生成集合中的一個值,如:scala> for(c <- "Hello"; i <- 0 to 1) yield (c + i).toChar
res7: String = HIeflmlmop
注:for推導式生成的集合與它第一個生成器是型別相容的。
函式
Scala除了支援方法外還支援函式。方法對物件進行操作,函式不是。要定義函式,你需要給出函式名、引數和函式體,如以下結構def abs(x: Double) = if(x > 0) x else -x 1.你必須給出所有引數的型別。不過,只要函式不是遞迴的,你就不需要指定返回型別。Scala編譯器可以通過=符號右側的表示式的型別推斷出返回型別。 2.如果函式體需要多個表示式完成,你可以使用程式碼塊。程式碼塊最後一個表示式的值就是函式的返回值。 如求前N項的和:def fac(n: Int) = {
var r = 0;
for(i <- 1 to n) r = r + i;
r
}
注:在cmd命令列執行時,因REPlL(讀取-求值-列印-迴圈)只能看到一行程式碼,所以在執行以上程式碼是可用貼上模式,鍵入:paste即可,把程式碼貼上進去,然後按下Ctrl+D。這樣REPL就會把程式碼塊當做一個整體分析。
對於遞迴函式,我們必須指定返回型別。如:
def fac(n: Int): Int = if(n<0) 1 else n * fac(n-1)
注:如果沒有返回值,Scala編譯器無法校驗n * fac(n-1)的型別是Int。
預設引數和帶名引數
在Scala中,呼叫某些函式時可以不顯式地給出所有引數值,對於這些函式我們可以使用預設引數。如:
def decorate(str: String, left: String = "[", right: String = "]") = left + str + right
注:left和right的預設引數分別為[和]。
呼叫方式:
1.使用decorate("Hello"),得到[Hello]。
2.使用decorate("Hello", "<<<", ">>>"),得到<<<Helo>>>。
3.提供引數值的時候指定引數名,即使用decorate(left = "<<<", str = "Hello", right = ">>>"),得到<<<Hello>>>。注:帶名引數不需要跟引數列表的順序保持一致。
4.混用未命名引數和帶名引數,即decorate("Hello", right = "]>>>"),得到[Hello]>>>。注:未命名引數要排在前面。
變長引數
Scala函式可以接受可變長度引數列表,如:
scala> :paste
// Entering paste mode (ctrl-D to finish)
def sum(args: Int*) = {
var result = 0;
for(arg <- args) result += arg;
result
}
// Exiting paste mode, now interpreting.
sum: (args: Int*)Int
scala> val s = sum(1, 2, 3, 4, 5, 6)
s: Int = 21
注:在複製完函式後,按Enter鍵後,使用Ctrl+D退出paste模式。在呼叫函式sum時,sum函式得到的是一個型別為Seq的引數。如果你已經有一個值的序列,則不能直接將它傳入sum函式,如sum(1 to 5)是錯誤的,在呼叫sum函式之前,你需要告訴編譯器你希望這個引數被當做引數序列處理,你可這樣呼叫,在表示式後面追加: _*,即使用sum(1 to 5: _*)呼叫即可。
過程
在Scala中,對於不返回值的函式有特殊的表示法。如果函式體包含在花括號當中但沒有前面的=號,那麼返回型別就是Unit。這樣的函式被稱做過程(procedure)。過程不返回值,我們呼叫它僅僅是為了它的副作用。
懶值
當val被宣告為lazy時,它的初始化將被推遲,直至我們首次對它取值。如,
lazy val words = scala.io.Source.formFile("")
注:formFile引數可為本地路徑-可參照scala檔案操作API。懶值並不是沒有額外開銷,我們每次訪問懶值,都會有一個方法被呼叫,而這個方法將會以執行緒安全的方式檢查該值是否已被初始化。異常
在Scala中,異常的工作機制和Java和C++一樣,即當你丟擲異常時,當前的運算被中止,執行時系統查詢可以接受該異常的異常處理器。控制權將在離丟擲點最近的處理器恢復。如果沒有找到符合要求的異常處理器,則程式退出。
和Java一樣,丟擲的物件必須是java.lang.Throwable的子類,但在Scala中throw表示式有特殊型別為Nothing。不過,與Java不同的是,Scala沒有"受檢"異常-你不需要宣告說函式或方法可能會丟擲某種異常。
在Scala中,捕獲異常的語法採用的是模式匹配的語法,與Java或C++一樣,更通用的異常應該排在具體的異常之後。注意,如果你不需要使用捕獲的異常物件,可以使用_來替代變數名。try/finally語句讓你可以釋放資源,不論有沒有異常發生。try/catch和try/finally的目的是互補的,try/catch語句處理異常,而try/finally語句在異常沒有被處理時執行某種動作。你也可以使用單個的try/catch/finally語句處理異常並釋放資源。