10

私は 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(())
      ()
    }
}

ご覧のとおり、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 

このエラーが発生するのはなぜですか? while ループよりもクリーンな方法でこれを回避する方法はありますか?

ありがとうございました

4

1 に答える 1

4

gen2まず、コンパイルに必要なものを見てみましょう。

object CpsConversions {

  import scala.collection.IterableLike
  import scala.util.continuations._

  implicit def cpsIterable[A, Repr](xs: IterableLike[A, Repr]) = new {
    def cps = new {
      def foreach[B](f: A => Any@cpsParam[Unit, Unit]): Unit@cpsParam[Unit, Unit] = {
        val it = xs.iterator
        while(it.hasNext) f(it.next)
      }
    }
  }
}

object GenTest {

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

それでは、何が起こっているのか見てみましょう。オリジナルgen2は次の行でコンパイルに失敗します:

ints.foreach((theInt) => yieldValue(theInt))

の型にyieldValueは注釈が含まれているため、継続プラグインはメソッドに@cpsParam渡された関数を次の型のいずれかに変換します。foreach

Int => Unit @cpsParam[Unit,Unit]

の階層を上に行くと、次のように定義されているList[Int]ことがわかります。foreach

foreach [U] (f: (Int) ⇒ U): Unit

型が一致せず、Scala は から への取得方法を認識しないため、これは問題Int => UですInt => Unit @cpsParam[Unit,Unit]。これを修正するために、暗黙的な変換に の CPS バージョンを追加しました。これには、 anyforeachを呼び出してアクセスできます。cpsIterableLike

この暗黙的な変換が明示的な呼び出しなしで実行できれば非常に素晴らしいことですがcps、Scala コンパイラーにそのような暗黙的な変換の適用可能性を認識させて、新しいものforeachをリストに追加する方法を見つけられませんでした。これは、コンパイラが継続プラグインを使用する順序に関係している可能性がありますが、私はこのプロセスについてほとんど知りません。

それで、それはすべてうまくいっていforeachます。あなたの質問は理解のために言及しています。これにはfilter、 、map、またはのいずれかflatMapを定義する必要があります (理解のために何が起こっているかによって異なります)。上記のコメントのリンクにこれらを実装しました。これにより、上記のCpsConversionsオブジェクトが拡張され、一般的な理解が可能になります。

于 2012-01-23T00:23:17.657 に答える