0Pricing
Scala for Backend Engineering & Functional Programming · 课时

多个生成器

嵌套迭代

多个生成器 是 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 反馈 — 无需本地设置。

此课程中的所有课时

  1. 展开 for
  2. 使用 if 筛选
  3. 多个生成器
  4. 与 Option 和 Future 一起使用
← 返回 Scala for Backend Engineering & Functional Programming