Scala中的循环匹配for

30 阅读2分钟

基础语法

val list = List(Some(1), None, Some(2), Some(3))

// 1. 提取 Some 中的值
for (Some(x) <- list) {
  println(x)
}
// 输出: 1, 2, 3
// None 被自动过滤掉

一、基本用法

1. 列表/集合中的模式匹配

val pairs = List((1, "a"), (2, "b"), (3, "c"))

// 提取元组元素
for ((num, letter) <- pairs) {
  println(s"数字: $num, 字母: $letter")
}

2. 过滤不匹配的元素

val mixed = List(1, "hello", 2, "world", 3)

// 只提取整数
for (x: Int <- mixed) {
  println(s"整数: $x")
}
// 输出: 整数: 1, 整数: 2, 整数: 3

二、常见使用场景

1. 处理 Option 类型

val options = List(Some("Alice"), None, Some("Bob"), None, Some("Charlie"))

// 方法一:使用模式匹配
for (Some(name) <- options) {
  println(s"名字: $name")
}

// 方法二:等价于
options.collect { case Some(name) => name }.foreach(println)

2. 处理 Map

val scores = Map("Alice" -> 85, "Bob" -> 92, "Charlie" -> 78)

// 提取键值对
for ((name, score) <- scores) {
  println(s"$name 的分数: $score")
}

// 过滤特定值
for ((name, score) <- scores if score > 80) {
  println(s"优秀: $name ($score)")
}

3. 嵌套结构匹配

val data = List(
  ("Alice", Some(25)),
  ("Bob", None),
  ("Charlie", Some(30))
)

// 多层模式匹配
for ((name, Some(age)) <- data) {
  println(s"$name 的年龄: $age")
}
// 输出: Alice 的年龄: 25, Charlie 的年龄: 30

三、结合守卫条件

val numbers = 1 to 10

// 提取偶数的平方
for (x <- numbers if x % 2 == 0) {
  println(s"$x 的平方: ${x * x}")
}

// 等价写法
for {
  x <- numbers
  if x % 2 == 0
  square = x * x
} {
  println(s"$x 的平方: $square")
}

四、高级模式匹配

1. 匹配特定模式

case class Person(name: String, age: Int)
val people = List(
  Person("Alice", 25),
  Person("Bob", 17),
  Person("Charlie", 30),
  Person("David", 16)
)

// 只匹配成年人
for (Person(name, age) <- people if age >= 18) {
  println(s"成年人: $name ($age 岁)")
}

2. 忽略部分元素

val triplets = List((1, 2, 3), (4, 5, 6), (7, 8, 9))

// 只取第一个和第三个元素
for ((first, _, third) <- triplets) {
  println(s"第一个: $first, 第三个: $third")
}

3. 使用 @ 绑定完整模式

val list = List(List(1, 2, 3), List(4, 5), List(6, 7, 8, 9))

for (innerList @ List(a, b, _*) <- list) {
  println(s"列表: $innerList, 前两个元素: $a, $b")
}

五、生成新集合(yield)

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

// 提取偶数并加倍
val doubledEvens = for {
  x <- numbers
  if x % 2 == 0
} yield x * 2

println(doubledEvens)  // List(4, 8)

// 复杂示例
case class User(name: String, age: Int, active: Boolean)
val users = List(
  User("Alice", 25, true),
  User("Bob", 17, true),
  User("Charlie", 30, false),
  User("David", 22, true)
)

val activeAdultNames = for {
  User(name, age, true) <- users
  if age >= 18
} yield name

println(activeAdultNames)  // List(Alice, David)

六、处理失败情况

val mixedData = List(
  Right("成功1"),
  Left("错误1"),
  Right("成功2"),
  Left("错误2"),
  Right("成功3")
)

// 只处理 Right 值
val successes = for (Right(value) <- mixedData) yield value
println(successes)  // List(成功1, 成功2, 成功3)

// 或者使用偏函数
val successes2 = mixedData.collect { case Right(value) => value }

七、多重生成器

val list1 = List(1, 2, 3)
val list2 = List('a', 'b', 'c')

// 生成所有组合
for {
  x <- list1
  y <- list2
} println(s"($x, $y)")

// 结合模式匹配
val optionalData = List(Some(1), None, Some(2))
val letters = List('a', 'b')

for {
  Some(num) <- optionalData  // 过滤掉 None
  letter <- letters
} println(s"$num$letter")
// 输出: 1a, 1b, 2a, 2b

八、实际应用示例

1. 数据清洗

val rawData = List(
  ("2023-01-01", Some(100.5), "USD"),
  ("2023-01-02", None, "USD"),
  ("2023-01-03", Some(200.0), "EUR"),
  ("2023-01-04", Some(150.75), "USD")
)

// 提取有效数据
val validTransactions = for {
  (date, Some(amount), currency) <- rawData
  if amount > 0
} yield (date, amount, currency)

println(s"有效交易数量: ${validTransactions.size}")

2. JSON 数据处理

case class Order(id: Int, customer: String, amount: Option[Double])

val orders = List(
  Order(1, "Alice", Some(100.0)),
  Order(2, "Bob", None),
  Order(3, "Charlie", Some(200.0)),
  Order(4, "David", Some(150.0))
)

// 计算有效订单总额
val totalAmount = (for {
  Order(_, _, Some(amount)) <- orders
} yield amount).sum

println(s"总金额: $totalAmount")

注意事项

  1. 类型安全for 中的模式匹配是类型安全的
  2. 过滤功能:不匹配的元素会被自动过滤掉,不会抛出异常
  3. 性能:对于大数据集,考虑使用 view 或迭代器
  4. 可读性:复杂的模式匹配可能影响可读性

最佳实践

  1. 用于数据提取和过滤
  2. 结合 yield 生成新集合
  3. 保持模式匹配简单
  4. 复杂的逻辑使用 match 表达式