You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Scala中for推导式与for循环执行逻辑异常问题咨询

Scala for推导式/循环的执行顺序解析

你看到的现象本质是Scala的for语法是map/flatMap/foreach等高阶函数的语法糖,而非传统命令式语言的嵌套循环,编译后的执行逻辑和直觉里的嵌套循环完全不同。

拆解第一个for推导式的编译逻辑

你的第一段代码:

def f(): List[(Int, Int)] = {
  val out = for 
    i <- 1 to 4
    p1 = println(s"i $i")
    j <- 2 to 3
    p2 = println(s"j $j")
  yield (i,j)
  println(out)
  out.toList
}

编译器会把它转换成类似这样的链式调用(简化后):

val out = (1 to 4)
  // 先遍历所有i,执行p1的println,生成包含所有i的中间集合
  .map { i =>
    println(s"i $i")
    i
  }
  // 再对中间集合里的每个i,遍历j的集合
  .flatMap { i =>
    (2 to 3).map { j =>
      println(s"j $j")
      (i, j)
    }
  }

这就解释了输出顺序:map会先完整遍历外层的1 to 4,把所有i x的打印全部执行完;之后flatMap再针对每个i,遍历2 to 3,重复打印j 2、j 3四次(对应4个i)。

第二个for-do循环的同理逻辑

你的第二段for循环代码:

def f(): Unit = {
for
  i <- List(10,20)
  p1 = println(s"i $i")
  j <- List(50, 60)
  p2 = println(s"i $i, j $j")
do {
  i+j
}
}

编译后等价于:

List(10,20)
  .map { i =>
    println(s"i $i")
    i
  }
  .foreach { i =>
    List(50,60).foreach { j =>
      println(s"i $i, j $j")
      i+j
    }
  }

同样是先通过map执行完所有外层i的打印,再通过foreach逐个处理每个i对应的内层j循环。

为什么最终结果和嵌套循环一致?

虽然中间副作用(println)的执行顺序变了,但flatMap和map的组合最终会生成和传统嵌套循环完全一致的元组集合——每个i都和所有j配对,只是生成过程是“先收集所有i,再逐个配对j”,而非“取一个i配完所有j,再取下一个i”。

如何让执行顺序和传统嵌套循环一致?

如果想要让i x的打印紧跟在对应的j循环之前,需要把print逻辑直接和i的生成器绑定到同一个上下文,比如用flatMap直接编写:

val out = (1 to 4).flatMap { i =>
  println(s"i $i")
  (2 to 3).map { j =>
    println(s"j $j")
    (i,j)
  }
}

此时执行顺序就会变成:i 1→j 2→j 3→i 2→j 2→j 3... 和传统嵌套循环的执行逻辑完全一致。

内容的提问来源于stack exchange,提问作者figs_and_nuts

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 02:15:06