【问题标题】:the for-comprehension with assignment loops in wrong order错误顺序的赋值循环的for-comprehension
【发布时间】:2015-06-08 15:13:36
【问题描述】:

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

for循环:

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

//将打印:

List(0)  
List(1)  
0  
1 

打印顺序错误!!

不是翻译成下面这样吗? for-loop的翻译:

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


List(0)  
0  
List(1)  
1  

---

我的问题:

  1. 为什么for循环不按直观顺序执行? (我期待结果是使用 foreach 的第二个示例)
  2. 我的翻译错了吗?
  3. 为什么我们必须在 for-condition 部分分配一些东西(例如_ = print())? (只是 print() 不会编译)

【问题讨论】:

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


    【解决方案1】:

    你给出的循环被翻译成以下内容:

    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 &lt;- 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 中才有意义;这在列表单子中也不是很有用。如果您在假设的 IO monad 中工作,您将能够执行以下操作:

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

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

    别问我为什么,就在the standard。恕我直言,像 Haskell 那样允许这样做实际上是有意义的。然而,我能想到的一个原因就是你正在尝试做的事情:将一种单子的副作用与另一种混合。他们本可以想一劳永逸地禁止这一点。这也可能与以下事实有关这种“混合”很多。

    【讨论】:

      【解决方案2】:

      这里的问题是您对 for 循环的翻译(主要来自对 _ = println(x) 的错误翻译)。

      Scala Specs可以看到(第90页):

      • 生成器p &lt;- e 后跟值定义p' = e' 被转换为以下值对生成器,其中xx' 是新名称:

      (p, p' ) &lt;- for (x@p &lt;- e) yield { val x'@p' = e' ; (x, x') }

      逐步应用转换,您会得到:

      for {
        (x, u) <- for (x <- l) yield {val u = println(x); (x, u)}
        y <- x
      } {println(y)}
      

      for {
        (x,u) <- l.map {case x => val u = println(x); (x,u)}
        y <- x
      } {println(y)}
      

      l.map     { case x => val u = println(x); (x,u) }
       .foreach { case (x,u) => for ( y <- x ) {println(y)} }
      

      l.map     { case x => val u = println(x); (x,u) }
       .foreach { case (x,u) => x.foreach {case y => println(y)} }
      

      scala> :paste
      // Entering paste mode (ctrl-D to finish)
      
          l.map     { case x => val u = println(x); (x,u) }
           .foreach { case (x,u) => x.foreach {case y => println(y)} }    
      
      // Exiting paste mode, now interpreting.
      
      List(0)
      List(1)
      0
      1
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-10-20
        • 1970-01-01
        • 2016-01-17
        • 1970-01-01
        • 2021-10-20
        • 2021-12-17
        • 2023-03-28
        • 1970-01-01
        相关资源
        最近更新 更多