多个生成器
嵌套迭代
多个生成器 是 CoddyKit 上的免费 Scala for Backend Engineering & Functional Programming 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Scala for Backend Engineering & Functional Programming 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Scala for Backend Engineering & Functional Programming 课程共包含 4 节课。
将嵌套迭代展平
for 推导式可以包含多个生成器。每个生成器都是一个通过 <- 遍历集合的行。它们组合后会产生所有可能的组合,相当于以扁平形式书写的嵌套循环。
object Main {
def main(args: Array[String]): Unit = {
val pairs = for {
x <- List(1, 2)
y <- List('a', 'b')
} yield (x, y)
println(pairs)
}
}这是笛卡尔积
大小分别为 m 和 n 的两个生成器会产生 m × n 个结果,即完整的笛卡尔积。第一个生成器是外层循环,第二个是内层循环。
object Main {
def main(args: Array[String]): Unit = {
val grid = for {
row <- 1 to 3
col <- 1 to 3
} yield s"r$row-c$col"
println(grid.mkString(", "))
}
}内层生成器可以依赖外层生成器
后面的生成器可以使用前面生成器绑定的值。普通的笛卡尔积无法做到这一点,这也是 for 推导式如此灵活的原因之一。
object Main {
def main(args: Array[String]): Unit = {
val upperTriangle = for {
i <- 1 to 4
j <- i to 4
} yield (i, j)
println(upperTriangle)
}
}三个或更多生成器
您可以按需叠加任意数量的生成器。使用三个生成器时,得到的是三层嵌套循环,可以生成元组或计算结果。
object Main {
def main(args: Array[String]): Unit = {
val triples = for {
a <- 1 to 2
b <- 1 to 2
c <- 1 to 2
} yield (a, b, c)
println(triples.size + " combinations")
triples.foreach(println)
}
}经典的毕达哥拉斯示例
一个著名的用法是查找毕达哥拉斯三元组。我们遍历三个数字,只保留满足 a*a + b*b == c*c 的组合,并将多个生成器与守卫结合起来。
object Main {
def main(args: Array[String]): Unit = {
val triples = for {
a <- 1 to 20
b <- a to 20
c <- b to 20
if a * a + b * b == c * c
} yield (a, b, c)
triples.foreach(println)
}
}脱糖后的形式
多个生成器会转换为嵌套的 flatMap 调用,最后接一个 map:
xs.flatMap(x => ys.flatMap(y => zs.map(z => ...)))
每增加一个生成器,就会多一层嵌套。
object Main {
def main(args: Array[String]): Unit = {
val sugar = for {
x <- List(1, 2)
y <- List(3, 4)
} yield x * y
val desugared = List(1, 2).flatMap(x => List(3, 4).map(y => x * y))
println(sugar == desugared)
}
}混合使用生成器和守卫
您可以自由交替使用生成器和守卫。放在两个生成器之间的守卫,会在内层循环运行之前剪掉不符合条件的外层值。
object Main {
def main(args: Array[String]): Unit = {
val result = for {
x <- 1 to 5
if x % 2 == 1
y <- 1 to 5
if y > x
} yield (x, y)
println(result)
}
}展平嵌套集合
当您拥有一个由集合组成的集合时,使用两个生成器即可自然地将其展平:第一个遍历外层集合,第二个遍历其中的每个内层集合。
object Main {
def main(args: Array[String]): Unit = {
val matrix = List(List(1, 2), List(3, 4), List(5, 6))
val flat = for {
row <- matrix
value <- row
} yield value
println(flat)
}
}构建字符串网格
多个生成器非常适合生成结构化输出,例如坐标或乘法表。
object Main {
def main(args: Array[String]): Unit = {
val table = for {
i <- 1 to 3
j <- 1 to 3
} yield s"$i*$j=${i * j}"
println(table.grouped(3).map(_.mkString(" ")).mkString("\n"))
}
}组合不同大小的集合
生成器不必具有相同的长度。每个生成器各取一个元素所形成的组合都会被生成。
object Main {
def main(args: Array[String]): Unit = {
val sizes = List("S", "M", "L")
val colors = List("red", "blue")
val products = for {
s <- sizes
c <- colors
} yield s"$c-$s"
println(products)
}
}顺序会影响输出序列
交换生成器的顺序不会改变组合的集合,但会改变它们的顺序,因为最后一个生成器变化得最快。
object Main {
def main(args: Array[String]): Unit = {
val ab = for { x <- List(1, 2); y <- List(9, 8) } yield (x, y)
val ba = for { y <- List(9, 8); x <- List(1, 2) } yield (x, y)
println(ab)
println(ba)
}
}快速检查
for { x <- List(1,2,3); y <- List('a','b') } yield (x, y) 会生成多少个元组?
回顾
您学习了多个生成器:
- 每个
<-行都会增加一层嵌套迭代 - 它们组合后会产生笛卡尔积
- 内层生成器可以依赖外层绑定的值
- 它们会脱糖为嵌套的
flatMap,再加上最后的map - 生成器的顺序会改变结果的排列顺序
常见问题解答
「多个生成器」课时是免费的吗?
是的 — 「多个生成器」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Scala for Backend Engineering & Functional Programming 课程的其余内容,请升级到 CoddyKit PRO。 Scala for Backend Engineering & Functional Programming 课程共包含 4 节课。
「多个生成器」这节课中我会学到什么?
嵌套迭代 你通过在浏览器中直接运行的动手代码来练习 Scala for Backend Engineering & Functional Programming,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Scala for Backend Engineering & Functional Programming 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Scala for Backend Engineering & Functional Programming 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。
「多个生成器」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Scala for Backend Engineering & Functional Programming 课中编写并运行代码吗?
能。每节 Scala for Backend Engineering & Functional Programming 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。