Scala 3 — 書籍

控制結構

語言

Scala 擁有您在其他程式語言中找到的控制結構,並具有強大的 for 運算式和 match 運算式

  • if/else
  • for 迴圈和運算式
  • match 運算式
  • while 迴圈
  • try/catch

以下範例示範這些結構。

if/else

Scala 的 if/else 控制結構看起來類似於其他語言。

if (x < 0) {
  println("negative")
} else if (x == 0) {
  println("zero")
} else {
  println("positive")
}
if x < 0 then
  println("negative")
else if x == 0 then
  println("zero")
else
  println("positive")

請注意,這確實是一個運算式,而不是陳述式。這表示它會傳回一個值,因此您可以將結果指定給變數

val x = if (a < b) { a } else { b }
val x = if a < b then a else b

正如您將在整本書中看到的,所有 Scala 控制結構都可以用作運算式。

運算式會傳回結果,而陳述式則不會。陳述式通常用於其副作用,例如使用 println 來列印至主控台。

for 迴圈和運算式

for 關鍵字用於建立一個 for 迴圈。此範例顯示如何列印 List 中的每個元素

val ints = List(1, 2, 3, 4, 5)

for (i <- ints) println(i)

程式碼 i <- ints 稱為 產生器。在任何產生器 p <- e 中,表達式 e 可以產生零個或多個與樣式 p 的繫結。產生器結尾括號後的程式碼是迴圈的 主體

val ints = List(1, 2, 3, 4, 5)

for i <- ints do println(i)

程式碼 i <- ints 稱為 產生器,而 do 關鍵字後的程式碼是迴圈的 主體

防護

您也可以在 for 迴圈內使用一個或多個 if 表達式。這些稱為 防護。此範例列印 ints 中所有大於 2 的數字

for (i <- ints if i > 2)
  println(i)
for
  i <- ints
  if i > 2
do
  println(i)

您可以使用多個產生器和防護。此迴圈會迭代數字 13,且對於每個數字,它也會迭代字元 ac。然而,它也有兩個防護,因此只有當 i 的值為 2j 為字元 b 時,才會呼叫列印陳述式

for {
  i <- 1 to 3
  j <- 'a' to 'c'
  if i == 2
  if j == 'b'
} {
  println(s"i = $i, j = $j")   // prints: "i = 2, j = b"
}
for
  i <- 1 to 3
  j <- 'a' to 'c'
  if i == 2
  if j == 'b'
do
  println(s"i = $i, j = $j")   // prints: "i = 2, j = b"

for 表達式

for 關鍵字有更強大的功能:當您使用 yield 關鍵字取代 do 時,您會建立 for 表達式,用於計算和產生結果。

幾個範例說明了這一點。使用與前一個範例相同的 ints 清單,此程式碼會建立一個新的清單,其中新清單中每個元素的值是原始清單中元素值的兩倍

scala> val doubles = for (i <- ints) yield i * 2
val doubles: List[Int] = List(2, 4, 6, 8, 10)
scala> val doubles = for i <- ints yield i * 2
val doubles: List[Int] = List(2, 4, 6, 8, 10)

Scala 的控制結構語法很靈活,而 for 表達式可以根據您的喜好以其他幾種方式撰寫

val doubles = for (i <- ints) yield i * 2
val doubles = for (i <- ints) yield (i * 2)
val doubles = for { i <- ints } yield (i * 2)
val doubles = for i <- ints yield i * 2     // style shown above
val doubles = for (i <- ints) yield i * 2
val doubles = for (i <- ints) yield (i * 2)
val doubles = for { i <- ints } yield (i * 2)

此範例顯示如何將清單中每個字串的第一個字元大寫

val names = List("chris", "ed", "maurice")
val capNames = for (name <- names) yield name.capitalize
val names = List("chris", "ed", "maurice")
val capNames = for name <- names yield name.capitalize

最後,這個 for 表達式會遍歷一個字串清單,並傳回每個字串的長度,但僅當長度大於 4

val fruits = List("apple", "banana", "lime", "orange")

val fruitLengths =
  for (f <- fruits if f.length > 4) yield f.length

// fruitLengths: List[Int] = List(5, 6, 6)
val fruits = List("apple", "banana", "lime", "orange")

val fruitLengths = for
  f <- fruits
  if f.length > 4
yield
  // you can use multiple lines
  // of code here
  f.length

// fruitLengths: List[Int] = List(5, 6, 6)

for 迴圈和表達式在本書籍的 控制結構 章節和 參考文件 中有更詳細的說明。

match 運算式

Scala 有 match 表達式,在最基本的用法中,它就像 Java switch 陳述式

val i = 1

// later in the code ...
i match {
  case 1 => println("one")
  case 2 => println("two")
  case _ => println("other")
}
val i = 1

// later in the code ...
i match
  case 1 => println("one")
  case 2 => println("two")
  case _ => println("other")

但是,match 實際上是一個表達式,表示它會根據模式比對傳回結果,你可以將結果繫結到變數

val result = i match {
  case 1 => "one"
  case 2 => "two"
  case _ => "other"
}
val result = i match
  case 1 => "one"
  case 2 => "two"
  case _ => "other"

match 不僅限於處理整數值,它可以用於任何資料類型

val p = Person("Fred")

// later in the code
p match {
  case Person(name) if name == "Fred" =>
    println(s"$name says, Yubba dubba doo")

  case Person(name) if name == "Bam Bam" =>
    println(s"$name says, Bam bam!")

  case _ => println("Watch the Flintstones!")
}
val p = Person("Fred")

// later in the code
p match
  case Person(name) if name == "Fred" =>
    println(s"$name says, Yubba dubba doo")

  case Person(name) if name == "Bam Bam" =>
    println(s"$name says, Bam bam!")

  case _ => println("Watch the Flintstones!")

事實上,match 表達式可以用於測試變數是否符合許多不同類型的模式。這個範例顯示 (a) 如何將 match 表達式用作方法的主體,以及 (b) 如何比對所有顯示的不同類型

// getClassAsString is a method that takes a single argument of any type.
def getClassAsString(x: Any): String = x match {
  case s: String => s"'$s' is a String"
  case i: Int => "Int"
  case d: Double => "Double"
  case l: List[_] => "List"
  case _ => "Unknown"
}

// examples
getClassAsString(1)               // Int
getClassAsString("hello")         // 'hello' is a String
getClassAsString(List(1, 2, 3))   // List

因為方法 getClassAsString 採用型別為 Any 的參數值,所以它可以被任何類型的模式分解。

// getClassAsString is a method that takes a single argument of any type.
def getClassAsString(x: Matchable): String = x match
  case s: String => s"'$s' is a String"
  case i: Int => "Int"
  case d: Double => "Double"
  case l: List[?] => "List"
  case _ => "Unknown"

// examples
getClassAsString(1)               // Int
getClassAsString("hello")         // 'hello' is a String
getClassAsString(List(1, 2, 3))   // List

方法 getClassAsString 採用型別為 Matchable 的值作為參數,它可以是支援模式比對的任何型別(有些型別不支援模式比對,因為這可能會破壞封裝)。

Scala 中的模式比對還有很多內容。模式可以巢狀,模式的結果可以繫結,模式比對甚至可以由使用者定義。請參閱 控制結構 章節中的模式比對範例,以取得更多詳細資訊。

try/catch/finally

Scala 的 try/catch/finally 控制結構讓你捕捉例外。它類似於 Java,但它的語法與 match 表達式一致

try {
  writeTextToFile(text)
} catch {
  case ioe: IOException => println("Got an IOException.")
  case nfe: NumberFormatException => println("Got a NumberFormatException.")
} finally {
  println("Clean up your resources here.")
}
try
  writeTextToFile(text)
catch
  case ioe: IOException => println("Got an IOException.")
  case nfe: NumberFormatException => println("Got a NumberFormatException.")
finally
  println("Clean up your resources here.")

while 迴圈

Scala 也有 while 迴圈結構。它的單行語法如下所示

while (x >= 0) { x = f(x) }
while x >= 0 do x = f(x)

Scala 3 為了相容性,仍然支援 Scala 2 的語法。

while 迴圈的多行語法如下所示

var x = 1

while (x < 3) {
  println(x)
  x += 1
}
var x = 1

while
  x < 3
do
  println(x)
  x += 1

自訂控制結構

多虧了依名稱參數、中綴表示法、流暢介面、可選括號、擴充方法和高階函式等功能,你也可以建立自己的程式碼,就像控制結構一樣。你將在 控制結構 章節中進一步瞭解這一點。

此頁面的貢獻者