延续和理解 - 什么是不相容?

Gui*_*Sim 10 continuations scala yield generator continuation-passing

我是Scala的新手,并试图绕过我试图重现yield returnC#语句的延续.

这篇文章之后,我写了以下代码:

package com.company.scalatest

import scala.util.continuations._;

object GenTest {

  val gen = new Generator[Int] {
    def produce = {
      yieldValue(1)
      yieldValue(2)
      yieldValue(3)
      yieldValue(42)
    }
  }
  // Does not compile :(

  //  val gen2 = new Generator[Int] {
  //    def produce = {
  //      var ints = List(1, 2, 3, 42);
  //
  //      ints.foreach((theInt) => yieldValue(theInt));
  //    }
  //  }

  // But this works?
  val gen3 = new Generator[Int] {
    def produce = {
      var ints = List(1, 2, 3, 42);
      var i = 0;
      while (i < ints.length) {
        yieldValue(ints(i));
        i = i + 1;
      }
    }
  }

  def main(args: Array[String]): Unit = {
    gen.foreach(println);
    //    gen2.foreach(println);
    gen3.foreach(println);
  }
}

abstract class Generator[E] {

  var loopFn: (E => Unit) = null

  def produce(): Unit @cps[Unit]

  def foreach(f: => (E => Unit)): Unit = {
    loopFn = f
    reset[Unit, Unit](produce)
  }

  def yieldValue(value: E) =
    shift { genK: (Unit => Unit) =>
      loopFn(value)
      genK(())
      ()
    }
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,gen2因为它不能编译而被注释掉.由于我可以使用while循环轻松迭代列表的内容(请参阅gen3参考资料),我希望foreach循环也能正常工作.

编译错误如下:

no type parameters for method foreach: (f: Int => B)Unit exist so that 
it can be applied to arguments (Int => Unit @scala.util.continuations.cpsParam[Unit,Unit])  
 --- because --- 
argument expression's type is not compatible with formal parameter type;  
found   : Int => Unit @scala.util.continuations.cpsParam[Unit,Unit]  
required: Int => ?B 
Run Code Online (Sandbox Code Playgroud)

为什么我会得到这个错误,有没有办法解决这个问题比使用while循环更干净?

谢谢

ear*_*las 4

gen2首先让我们看看编译需要什么。

\n\n
object CpsConversions {\n\n  import scala.collection.IterableLike\n  import scala.util.continuations._\n\n  implicit def cpsIterable[A, Repr](xs: IterableLike[A, Repr]) = new {\n    def cps = new {\n      def foreach[B](f: A => Any@cpsParam[Unit, Unit]): Unit@cpsParam[Unit, Unit] = {\n        val it = xs.iterator\n        while(it.hasNext) f(it.next)\n      }\n    }\n  }\n}\n\nobject GenTest {\n\n  import CpsConversions.cpsIterable\n  val gen2 = new Generator[Int] {\n    def produce = {\n      var ints = List(1, 2, 3, 42)\n      ints.cps.foreach((theInt) => yieldValue(theInt))\n    }\n  }\n
Run Code Online (Sandbox Code Playgroud)\n\n

现在让我们看看到底发生了什么。原始文件gen2无法在以下行编译:

\n\n
ints.foreach((theInt) => yieldValue(theInt))\n
Run Code Online (Sandbox Code Playgroud)\n\n

由于 的类型yieldValue包含@cpsParam注释,因此延续插件将传递给该foreach方法的函数转换为以下类型之一:

\n\n
Int => Unit @cpsParam[Unit,Unit]\n
Run Code Online (Sandbox Code Playgroud)\n\n

在 的层次结构中List[Int],您将看到foreach定义为:

\n\n
foreach [U] (f: (Int) \xe2\x87\x92 U): Unit\n
Run Code Online (Sandbox Code Playgroud)\n\n

这是一个问题,因为类型不匹配并且 Scala 不知道如何从Int => UInt => Unit @cpsParam[Unit,Unit]。为了解决这个问题,我在隐式转换中添加了 CPS 版本foreach,您可以通过调用cps任何IterableLike.

\n\n

如果这种隐式转换可以在没有显式调用的情况下完成cps,那就太好了,但我还没有找到一种方法让 Scala 编译器认识到这种隐式转换的适用性,以将 new 引入foreach到您的列表中。这可能与编译器使用延续插件的顺序有关,但我对这个过程知之甚少,无法确定。

\n\n

所以这对 来说一切都很好foreach。您的问题提到了理解,这将需要定义filtermap或中的任何一个flatMap(取决于您的理解中发生的情况)。我已经在上面评论的链接中实现了这些,它扩展了CpsConversions上面的对象以允许一般理解。

\n