scala - 赋值循环的 for 理解顺序错误

标签 scala for-loop functional-programming for-comprehension

给定 val l = List( List(0), List(1) )

for循环:

for {
  x <- l
  _ = println(x)
  y <- x
} {println(y)}

//会打印:

List(0)  
List(1)  
0  
1 

打印顺序错误!

不就是翻译成下面的吗? for循环的翻译:

l.foreach(
  x => {
    println(x)
    x.foreach(y => println(y))
  }
)


List(0)  
0  
List(1)  
1  

---

我的问题:

  1. 为什么 for 循环不按直观顺序执行? (我期望结果是使用 foreach 的第二个示例)
  2. 我的翻译有误吗?
  3. 为什么我们必须在 for 条件部分分配一些东西(例如 _ = print())? (仅 print() 无法编译)

最佳答案

您给出的循环将被转换为以下内容:

l.map(((x) => {
  val x$1 = println(x);
  scala.Tuple2(x, x$1)
})).foreach(((x$2) => x$2: @scala.unchecked match {
  case scala.Tuple2((x @ _), (x$1 @ _)) => x.foreach(((y) => println(y)))
}))

您可以通过 quasiquoting 找到类似的信息他们在口译员中。

发生的情况是,赋值与生成器之前关联起来——就像你写 for (x <- l; h = x.head) 一样。 ,这两者的耦合显然是必要的。

如果您希望每个后续生成器都发生副作用,则必须编写以下内容:

for {
  x <- l
  y <- {println(x); x}
} {println(y)}

这会产生您想要的打印输出,并且编译后完全符合您的预期:

l.foreach(((x) => {
  println(x);
  x
}.foreach(((y) => println(y)))))

至于为什么在生成器中显式丢弃参数是必要的——有两个问题。第一,println处于不同的单子(monad)中。显然,仅为了在 monad 理解中起作用而运行表达式仅在同一个 monad 中才有意义;这在列表单子(monad)中也不是很有用。如果您在假设的 IO monad 中工作,您将能够执行以下操作:

for {
  y <- readLn()
  _ <- printLn(x)
}

但第二个问题来了:Scala 甚至不允许丢弃单元结果,我们仍然必须对 _ 进行模式匹配。

别问我为什么,就在 the standard 里。恕我直言,允许这样做实际上是有意义的,就像 Haskell 所做的那样。然而,我能想到的一个原因正是您正在尝试做的事情:将一个单子(monad)的副作用与另一个单子(monad)的副作用混合。他们本可以想一劳永逸地禁止这一点。这也可能与 Scala 中 for 推导式的重写比 Haskell 中更多地基于鸭子类型有关,可能有更多的极端情况,甚至在类型检查之前发生(据我所知),这会使情况复杂化这样的“混合”很多。

关于scala - 赋值循环的 for 理解顺序错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29433727/

相关文章:

arrays - 为什么 `Array(0,1,2) == Array(0,1,2)`没有返回预期结果?

scala - Scala 中是否有 GroovyScriptEngine 和 GroovyClassLoader 之类的东西?

scala - 隐式编译器错误?

python - 仅在 python 中使用更快的 for 循环

functional-programming - 匹配比较 OCaml

java - LambdaJ:为什么我们不能在 select 子句中应用操作?

scala - Scala 中的密封特征和抽象密封类之间的具体区别是什么?

scala - 如何在 sbt 测试中仅显示失败的测试?

java - 翻译对象、停止和继续-处理

loops - 如何在 Julia 中实现递减循环?