1. 程式人生 > >如何在Scala中使用條件表示式

如何在Scala中使用條件表示式

條件表示式

Scala的if/else語法結構和Java或C++一樣。不過,在Scala中if/else表示式有值,這個值就是跟在if或else之後的表示式的值。例如:

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。

(之前已經提過,Scala中的分號絕大多數情況下不是必需的。)

Java和C++有一個?:操作符用於同樣目的。如下表達式

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

等同於Scala表示式 if (x > 0) 1 else −1。不過,你不能在?:表示式中插入語句。Scala的if/else將在Java和C++中分開的兩個語法結構if/else和?:結合在了一起。

在Scala中,每個表示式都有一個型別。舉例來說,表示式 if (x > 0) 1 else −1的型別是Int,因為兩個分支的型別都是Int。混合型別表示式,比如:

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

上述表示式的型別是兩個分支型別的公共超型別。在本例中,其中一個分支是java.lang.String,而另一個分支是Int。它們的公共超型別叫做Any。(詳細內容參見8.11節。)

如果else部分缺失了,比如:

if (x > 0) 1

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

if (x > 0) 1 else ()

你可以把()當做是表示“無有用值”的佔位符,將Unit當做Java或C++中的void。(從技術上講,void沒有值但是Unit有一個表示“無值”的值。如果你一定要深究的話,這就好比空的錢包和裡面有一張寫著“沒錢”的無面值鈔票的錢包之間的區別。)

說明:Scala沒有switch語句,不過它有一個強大得多的模式匹配機制,我們將在第14章中看到。在現階段,用一系列的if語句就好。

注意: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中才會有這個顧慮。在被編譯的程式中,解析器會找到下一行的else。

提示:如果你想在REPL中貼上成塊的程式碼,而又不想擔心REPL的近視問題,可以使用貼上模式。鍵入:

:paste

把程式碼塊貼上進去,然後按下Ctrl+D。這樣REPL就會把程式碼塊當做一個整體來分析。

語句終止

在Java和C++中,每個語句都以分號結束。而在Scala中——與JavaScript和其他指令碼語言類似——行尾的位置不需要分號。同樣,在}、else以及類似的位置也不必寫分號,只要能夠從上下文明確地判斷出這裡是語句的終止即可。

不過,如果你想在單行中寫下多個語句,就需要將它們以分號隔開。例如:

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

我們需要用分號將 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++的程式設計師一開始並不適應省去分號的做法。如果你傾向於使用分號,用就是了——它們沒啥壞處。

塊表示式和賦值

在Java或C++中,塊語句是一個包含於{ }中的語句序列。每當你需要在邏輯分支或迴圈中放置多個動作時,你都可以使用塊語句。

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

這個特性對於那種對某個val的初始化需要分多步完成的情況很有用。例如,

val distance = { val dx = x - x0; val dy = y - y0; sqrt(dx * dx + dy * dy) }

{ }塊的值取其最後一個表示式,在此處以粗體標出。變數dx和dy僅作為計算所需要的中間值,很乾淨地對程式其他部分而言不可見了。

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

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

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

的值是Unit型別的。這沒有問題,只是當我們定義函式時需要意識到這一點。

由於賦值語句的值是Unit型別的,別把它們串接在一起。

x = y = 1 // 別這樣做

y = 1的值是(),你幾乎不太可能想把一個Unit型別的值賦值給x。(與此相對應,在Java和C++中,賦值語句的值是被賦的那個值。在這些語言中,將賦值語句串接在一起是有意義的。)

輸入和輸出

如果要列印一個值,我們用print或println函式。後者在列印完內容後會追加一個換行符。舉例來說,

print("Answer: ")

println(42)

與下面的程式碼輸出的內容相同:

println("Answer: " + 42)

另外,還有一個帶有C風格格式化字串的printf函式:

printf("Hello, %s! You are %d years old.\n", "Fred", 42)

你可以用readLine函式從控制檯讀取一行輸入。如果要讀取數字、Boolean或者是字元,可以用readInt、readDouble、readByte、readShort、readLong、readFloat、readBoolean或者readChar。與其他方法不同,readLine帶一個引數作為提示字串:

val name = readLine("Your name: ")

print("Your age: ")

val age = readInt()

printf("Hello, %s! Next year, your will be %d.\n", name, age + 1)

本文節選自《快學Scala

電子工業出版社出版

(美)霍斯曼(Horstmann,C.S.)著

高宇翔譯