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
相关产品推荐
相关产品推荐

