Skip to content

Scala 循环类型

虽然 Scala 支持像 while 和 do-while 这样的命令式循环结构,但惯用的 Scala 代码通常更倾向于使用函数式方法进行迭代,主要是通过 for 推导式 (for comprehensions) 和集合方法 (foreach, map, filter 等) 来实现。

Scala 的 for 推导式比其他语言中的传统 for 循环强大得多。它是对集合或定义了这些方法的其他类型上的 foreach, map, flatMap 和 filter 操作组合的语法糖。

val numbers = List(1, 2, 3, 4, 5)
println("Iterating through numbers:")
for (num <- numbers) {
println(num)
}

输出:

Iterating through numbers:
1
2
3
4
5
val rangeInclusive = 1 to 5 // 包含边界的 Range: 1, 2, 3, 4, 5
val rangeExclusive = 1 until 5 // 不包含边界的 Range: 1, 2, 3, 4
println("\nIterating through range (1 to 3):")
for (i <- 1 to 3) {
println(s"Iteration $i")
}

输出:

Iterating through range (1 to 3):
Iteration 1
Iteration 2
Iteration 3

你可以在 for 推导式中添加 if 条件 (Guards) 来过滤元素。

println("\nEven numbers:")
for (num <- numbers if num % 2 == 0) {
println(num)
}

输出:

Even numbers:
2
4

使用 yield 关键字可以将 for 推导式变成一个表达式,该表达式根据迭代产生一个新集合。

val squaredNumbers = for (num <- numbers) yield num * num
println(s"\nSquared numbers: $squaredNumbers") // 输出: Squared numbers: List(1, 4, 9, 16, 25)
val descriptions = for {
num <- numbers
if num > 2
} yield s"Number $num"
println(s"Descriptions: $descriptions") // 输出: Descriptions: List(Number 3, Number 4, Number 5)

你可以使用多个 <- 子句来迭代组合。

val letters = List('a', 'b')
val numbersForPairing = List(1, 2)
val pairs = for {
letter <- letters
number <- numbersForPairing
} yield (letter, number)
println(s"\nPairs: $pairs") // 输出: Pairs: List((a,1), (a,2), (b,1), (b,2))

while 循环在条件为真时重复执行代码块。条件在每次迭代之前检查。

var i = 0
println("\nWhile loop:")
while (i < 3) {
println(s"i = $i")
i += 1 // 重要: 确保条件最终会变为假!
}

输出:

While loop:
i = 0
i = 1
i = 2

与 while 类似,但条件在循环体执行之后检查。这保证了循环体至少执行一次。

var j = 5
println("\nDo-while loop:")
do {
println(s"j = $j")
j += 1
} while (j < 3) // 条件最初为假,但循环会运行一次

输出:

Do-while loop:
j = 5

注意: 在惯用的函数式 Scala 代码中,while 和 do-while 循环不太常见,因为它们通常涉及可变状态 (i, j)。如果可能,优先使用函数式替代方案,如递归或集合方法。

Scala 没有像 Java 或 C++ 那样的 break 或 continue 关键字。要提前退出循环,可以采用以下方法:

  • 函数式风格: 使用集合方法如 find, takeWhile, exists 或带有基本情况的递归来重构循环。
  • scala.util.control.Breaks: 提供 break 和 breakable 结构。然而,通常不鼓励使用它,因为它依赖于抛出异常来进行控制流,且函数式风格较弱。
  • 从方法返回: 将循环放在一个方法内,并在满足退出条件时使用 return (仅当循环是方法的主要部分时才有效)。

函数式风格示例 (查找第一个偶数):

val data = List(1, 3, 5, 6, 7, 8)
val firstEven = data.find(_ % 2 == 0) // 返回 Option[Int]
println(s"\nFirst even number: $firstEven") // 输出: First even number: Some(6)