Scala學習(二)控制結構和函數

1.條件表達式

Scala的if/else語法結構和Java同樣。不過,在Scala中if/else表達式有值,這個值就是跟在if或else以後的表達式的值。例如:java

if(x>0) 1 else -1

或者將表達式的值賦值給變量:程序員

val s = if(x>0) 1 else -1

這與以下語句的效果同樣:數組

if(x>0) s = 1 else s = -1

不過,第一種寫法更好,由於它能夠用來初始化一個val常量。而第二種寫法中,s必須是var變量。安全

--------------------------------函數

在Scala中if/else有着和Java或者C++的三元表達式相同的功能,例如this

x>0 ? 1 : -1 //Java或C++

而在Scala中,咱們能夠這樣:編碼

if(x>0) "hello" else -1

在Scala中每一個表達式都有一種類型,表達式的類型能夠相同能夠不相同,這點與Java有所不一樣。不一樣類型表達式的if/else咱們稱爲混合表達式,spa

接收他們的值或變量的類型爲公共類型Any(至關於Java中的Object)線程

--------------------------------scala

若是else部分缺失了,例如:

if(x>0) 1

那麼可能if沒有輸出值,可是在Scala中,每一個表達式都應該有值。這個問題的解決方案是引入一個Unit類,寫作()。不帶餓死了的這個if語句等同於

if(x>0) 1 else ()

你能夠把()當作「無有用值」的佔位符,將Unit當作Java或C++中的void。

注:Scala沒有switch語句

注:REPL(解析器)比起編譯器來講,它只可以按行執行程序,例如:

if (x > 0) 1 

else if (x == 0) 0 else -1

這時REPL會執行if (x>0) 1 而後顯示結果。以後的else他將會報錯。

若是你想在else前換行的話,要用花括號:

if (x > 0) {1 

}else if (x == 0) 0 else -1

只有在REPL中才會有這樣的顧慮,其餘地方你們大可沒必要擔憂

提示:若是想在REPL中粘貼成塊的代碼又不想讓REPL按行執行,可使用粘貼模式。鍵入

:paste

把代碼粘貼進去,而後Ctrl+D。這樣REPL就會把代碼塊當作一個總體分析。

2.語句終止

在Java和C++中,每一個語句都要以分號結束。而如今Scala中與JavaScript和其餘腳本相似,行位不須要設置分號。一樣在}、else以及相似的位置也沒必要寫分號,只要可以從上下文明確的判斷出這裏語句是終止的便可。

不過,若是你想在單行中寫下多個語句,就須要將他們以分號分開。例如:

if(n > 0) { r = r * n; n -=1 }

若是你在寫一個較長的語句,須要分兩行寫的話,就須要確保第一行以一個不能用做語句結尾的符號結尾。一般來講一個比較好的選擇是操做符:

s = s0 + (v - v0) * t +       // +來告訴解析器這裏不是語句的末尾

0.5 * (a - a0) * t * t

在實際的編碼時,長表達式一般涉及函數或方法調用,Scala程序猿更傾向於使用Kernighan&Ritchie風格的花括號

if(n >0){

  r = r * n

  n -= 1

}

以‘{’結束的行很清楚的表達了後面還有內容,直到碰見匹配的‘}’

許多來自Java或C++的程序員一開始並不適應省去分號的作法。若是你更傾向於使用分號,用了就是了,沒啥壞處。

3.塊表達式和賦值

在Java或C++中,塊語句是包含於{}中的語句序列。每當你須要在邏輯分支或循環中放置多個動做時,你均可以使用塊語句。

在Scala中,{}塊包含一系列的表達式,其中結果也是一個表達式。塊中最後一個表達式的值就是塊兒的值。

--------------------------------

在Scala中,賦值動做自己是沒有值得——或者,更嚴格地說,他們的值是Unit類型的。你應該記得,Unit類型等同於Java和C++中的void,而這個類型的值只有一個值,寫作()。

一個賦值語句結束的塊,好比

{r = r * n; n -= 1}

的值是Unit類型的。這沒有問題,只是當咱們定義函數時須要意識到這一點。

注:因爲賦值語句的值是Unit類型的,別把他們串接在一塊兒。

x = y = 1 //別這麼作

y = 1 的值是(),你不可能想把一個Unit類型的值賦值給x。(在Java或者C++中,上面的作法是能夠作到x、y同時賦值的)

4.輸入和輸出

若是要打印一個值,咱們用print或者println以及printf函數。與Java中的用法相同,這裏不作過多解釋。

--------------------------------

你能夠用readLine函數從控制檯讀取一行輸入。若是要讀取數字、Boolean或者是字符,能夠用readInt、readDouble、readByte、readShort、readLong、readFloat、readBoolean或者readChar。

與其餘方法不一樣,readLine帶一個參數做爲提示字符串:

 

5.循環

Scaca中擁有與Java和C++相同的while和do循環。例如:

--------------------------------

Scala中沒有與Java相同的for結構:for(初始化變量;檢查變量是否知足條件;更新變量)。

在Scala中只有這樣的for循環語法:

for(i ← 表達式)      //i不須要定義,也能夠因此取名

接下來咱們用RichInt類的to方法,去演示Scala中for的用法:

還有另外一種遍歷方法until,它經常使用於遍歷字符串或者數組,同上面的to方法的例子比較,能看出他們兩個的區別

to方法:包含頭和尾的遍歷(至關於<=)

until方法:包含頭不包含尾的遍歷(至關於<)

注:遍歷字符串的兩種方法:

1.用until方法

2.直接遍歷

--------------------------------

Scala中並無提供break或者continue語句來退出循環。若是須要break時咱們能夠採起以下選項:

  1. 使用Boolean型控制變量
  2. 使用嵌套函數——你能夠從函數當中return
  3. 使用Breaks對象中的break方法

break方法使用示例:

 

6.高級for循環和for推導式

Scala中的for循環要比Java功能豐富的多,好比:

1.咱們能夠在for循環中添加多個 變量←表達式 這種形式的生成器,用分號分隔開:

2.每個生成器均可以帶一個守衛,以if開頭的Boolean表達式:注意if前沒有分號

3.你可使用任意多的定義,引入能夠在循環中使用的變量

4.若是for循環以yield開始,則該循環會構造出一個集合,每次迭代生成集合中的一個值

這類循環叫作for推導式,for推導式生成的集合與它的第一個生成器的類型是兼容的

7.函數

Scala除了方法還支持函數,方法對對象進行操做,函數不是。C++也有函數,在Java中咱們只能經過靜態方法來模擬函數。

要定義函數,完整格式以下:

def  [函數名稱]([參數名稱]:[參數類型]...): [返回值類型] = {

    函數體

}

例如:

def add(x : Int , y : Int) : Int = {

    var z = x + y;

    return z;

}

因爲{}塊中最後一個表達式的值就是塊兒的值。所以咱們能夠省略return。

def add(x : Int , y : Int) : Int = {

    var z = x + y;

    z;

}

在進一步簡化:

def add(x : Int , y : Int) : Int = x + y

在Scala中,只要函數不是遞歸的,咱們就不須要指定返回類型。Scala編譯器能夠經過 = 右側的表達式類型推斷出返回值類型。所以最終函數簡化以下:

def add(x : Int , y : Int)  = x + y

若是是遞歸函數,咱們就必需要指定返回值類型了。

def fac(n : Int) : Int = if(n <= 0) 1 else n * fac( n - 1)

說明:雖然在帶名函數中使用return並無什麼不對,可是咱們最好適應沒有return的日子。很快,你就會使用大量的匿名函數,這些函數中return並不返回值給調用者。

它跳出到包含它的帶名函數中。咱們能夠把return當作是函數版break語句,盡在須要時使用。

8.默認參數和帶名參數

咱們在調用某些函數時並不顯示的給出全部參數值,對於這些函數咱們可使用默認參數值。例如:

def decorate(prefix : String, str : String, suffix : String) = {

    prefix + str + suffix

}

在調用上面的decorate函數的時候咱們必須給出全部的參數,不然函數將會執行錯誤,那若是此時咱們只想給出一個參數就能讓其運行咱們該如何作呢?那就是默認參數值:

def decorate(prefix : String = "[[", str : String, suffix : String = "]]") = {

    prefix + str + suffix

}

咱們能夠在定義函數入參時先給其賦值,從而作到默認參數值的功能,可是另外一個問題又來了,做爲程序員咱們都知道調用方法或者函數的時候,參數都是按順序寫入的,咱們如何作到給指定位置的入參賦值呢?Scala幫咱們作到了這一點:

如圖,咱們能夠向表達式同樣爲指定參數賦值,這樣咱們就能夠無視參數的順序來調用函數或者方法了,這就是帶名參數

9.變長參數

咱們知道在Java中,邊長參數用 ... 來表示:

public void method(int a ...){

    方法體...

}

而在Scala中,邊長參數用* 來表示,好比咱們建立一個計算多個數相加的方法,參數不肯定。

def add(a : Int*) = {

    var sum = 0;

    for(x <- a) sum += x;

    sum;

}

對於上面的add方法,若是你已經有了一個有值序列,則不能直接將它傳入上面的大的方法,好比:

val a = add(1 to 5)  //錯誤

由於咱們的add方法的入參是Int類型的序列,而1 to 5 的類型爲:

他並非add方法的入參類型所以直接傳入必定有問題,須要進行類型的轉換,那麼範圍類型如何轉換成入參序列呢?Scala中爲咱們提供了一種語法,追加 :_*

val a = add(1 to 5 :_* )

兩種狀況對比:

在遞歸的定義中咱們會用到上述語法:

def recursiveSum(args : Int*): Int = {

    if(args.length == 0) 0

    else args.head = recursiveSum(args.tail:_*)

}

在這裏,序列的head是他的首個元素,而tail是全部其餘元素的序列,這又是一個序列,咱們用 :_* 來將它轉換成參數序列。

10.過程

Scala對於不返回值得函數有特殊的表示法。若是函數體包含在花括號當中但沒有前面的=號,那麼返回類型就是Unit。這樣的函數被稱爲過程。過程不返回值,咱們調用它僅僅是爲了他的輔助做用。

例如:

def out(a : Int){

    print(a)

}

或者咱們能夠顯示聲明Unit返回類型:

def out(a : Int): Unit = {

    print(a)

}

11.懶值

當val 被聲明爲lazy時,他的初始化將被延遲,也就是懶加載,知道咱們首次對它進行取值,例如

//在words被定義時取值

val words = scala.io.Source.fromFile("C:\\Users\\Administrator\\Desktop\\wordCount.txt").mkString

//在words被首次使用時取值

lazy val words = scala.io.Source.fromFile("C:\\Users\\Administrator\\Desktop\\wordCount.txt").mkString

//在每一次words被使用時取值

def words = scala.io.Source.fromFile("C:\\Users\\Administrator\\Desktop\\wordCount.txt").mkString

你能夠把懶值當作是介於val和def的中間狀態

--------------------------------

說明:懶值並非沒有額外開銷。咱們每次訪問懶值,都會有一個方法被調用,而這個方法將會以線程安全的方式檢查該值是否已被初始化。

12.異常

Scala的異常工做機制和Java或者C++同樣。拋出異常時你能夠這樣:

throw new Exception("this is a exception")

和Java同樣,排出的對象必須是java.lang.Throwable的子類。不過,與Java不一樣的是,Scala沒有「受檢」異常,你不須要聲明說函數或方法可能拋出某種異常。

--------------------------------

throw表達式有特殊的返回類型Nothing。好比:

if(x >= 0){

    sqrt(x)

}else throw new IllegalArgumentException(" x should not be negative")

第一個分支類型爲Double,第二個分支類型爲Nothing。所以,if/else表達式的類型是Double

--------------------------------

捕獲異常的語法採用的是模式匹配的語法

try{

    "Hello".toInt

} catch {

    case _ : Exception       => println("xxxxxxx")

    case ex : IOException => ex.printStackTrace()

}

和Java或C++同樣,更通用的異常應該排在更具體的異常以後。

若是你不須要使用捕獲的異常對象,可使用_來代替變量名。

--------------------------------

try/finally語句讓你能夠釋放資源,不論有沒有發生異常。這點和Java同樣。

咱們也能夠下車和Java中同樣的try/catch/finally語句:

try{...}catch{...}finally{...}

相關文章
相關標籤/搜索