while 迴圈


while迴圈根據所指定的條件式來判斷是否執行迴圈本體,例如以下是個求最大公因數的程式:
println("輸入兩個數字...")
var m = readInt
var n = readInt
while(n != 0) {
val r = m % n
m = n
n = r
}
println("GCD:" + m)

如果迴圈本體只有一個陳述句,則while的{ }可以省略不寫;while迴圈為前測式迴圈,因為它在迴圈執行前就會進行條件判斷,而do while為後測式迴圈,它會先執行迴圈本體,然後再進行條件判斷,例如:
do {
print("輸入數字:")
printf("輸入數為 %s%n", if(readInt % 2 == 0) "偶數" else "奇數")
print("繼續?(Yes/No)")
} while(readLine == "Yes")

這個程式會判斷使用者輸入數為奇數或偶數,並詢問是否繼續,如果輸入Yes則繼續,輸入No離開。

在Scala中,while並不是運算式,因為它不會傳回運算的結果。不過若要更精確地說,while傳回(),類型是Unit,稱之為單元值(Unit value),在Scala中,一個操作不需傳回任何值時就傳回Unit,你沒辦法拿Unit作什麼事,所以傳回Unit其實就等同於沒有傳回值。

因為while不會有傳回值,所以使用while處理某件事時,經常伴隨著var的存在,如先前所說的,Scala鼓勵你使用val,因為var允許變數被重新設值,這使得演算不易被拆解為獨立的任務,追蹤變數值往往也會是件困難的事。

由於while往往伴隨著var的使用,因此Scala中鼓勵你將while迴圈的任務嘗試使用遞迴來解決,例如先前求最大公因數的例子,若可以定義為函式並使用遞迴,則可以改寫為以下(記得if是個運算式,所以可以有傳回值):
def gcd(m: Int, n: Int): Int = if(n == 0) m else gcd(n, m % n)
println("輸入兩個數字...")
println("GCD:" + gcd(readInt, readInt))

可以與同樣定義gcd函式,但使用while迴圈的版本作個比較,可以看出上面這個版本相較之下簡潔且容易閱讀:
def gcd(m: Int, n: Int) = {
var a = m
var b = n
while(b != 0) {
val r = a % b
a = b
b = r
}
a
}
println("輸入兩個數字...")
println("GCD:" + gcd(readInt, readInt))

在Scala中,指定運算也是不傳回值的操作,如果你熟悉Java,有個在Java中很常的操作,在Scala中就行不通:
var line = ""
while((line = readLine) != "") {
    println("Echo: " + line)
}

這個片段很類似在Java讀取輸入的程式,你的目的是希望使用者直接按下Enter後(readLine會傳回"")離開程式,但這個片段在Scala中會有以下的警訊:
warning: comparing values of types Unit and java.lang.String using `!=' will always yield true
while((line = readLine) != "") {

這是因為指定運算的結果會是Unit,所以實際上,你的while迴圈等於是:
while(() != "") {
    ...
}

() != "" 的結果永遠會是true,你等於是在寫無窮迴圈,所以程式並不會如你所願在按下Enter後結束