6

以下に示すように、多かれ少なかれオブジェクトインスタンスをモジュール/ファンクターとして使用したい:

abstract class Lattice[E] extends Set[E] {
  val minimum: E
  val maximum: E
  def meet(x: E, y: E): E
  def join(x: E, y: E): E
  def neg(x: E): E
}

class Calculus[E](val lat: Lattice[E]) {
  abstract class Expr
  case class Var(name: String) extends Expr {...}
  case class Val(value: E) extends Expr {...}
  case class Neg(e1: Expr) extends Expr {...}
  case class Cnj(e1: Expr, e2: Expr) extends Expr {...}
  case class Dsj(e1: Expr, e2: Expr) extends Expr {...}
}

格子ごとに異なる微積分インスタンスを作成できるようにします (実行する操作には、格子の最大値と最小値の情報が必要です)。同じ微積分の式を混合できるようにしたいが、異なる式を混合することは許可されていません。ここまでは順調ですね。微積分インスタンスを作成できますが、問題は、それらを操作する他のクラスに関数を記述できないことです。

たとえば、ファイルから式を読み取って返すパーサーを作成しようとしています。また、ScalaCheck を使用したテストで使用するランダム式ジェネレーターを作成しようとしていました。関数が Expr オブジェクトを生成するたびに、関数の外では使用できないことがわかりました。Calculus インスタンスを作成し、Expr オブジェクトを生成する関数に引数として渡しても、関数の戻り値は、関数の外部で作成されたオブジェクトと同じ型として認識されません。

多分私の英語は十分に明確ではありません.私がやりたいことのおもちゃの例を試してみましょう.

def genRndExpr[E](c: Calculus[E], level: Int): Calculus[E]#Expr = {
  if (level > MAX_LEVEL) {
    val select = util.Random.nextInt(2)
    select match {
      case 0 => genRndVar(c)
      case 1 => genRndVal(c)
    }
  }
  else {
    val select = util.Random.nextInt(3)
    select match {
      case 0 => new c.Neg(genRndExpr(c, level+1))
      case 1 => new c.Dsj(genRndExpr(c, level+1), genRndExpr(c, level+1))
      case 2 => new c.Cnj(genRndExpr(c, level+1), genRndExpr(c, level+1))
    }
  }
}

さて、上記のコードをコンパイルしようとすると、たくさんの

エラー: タイプが一致しません。  
 見つかった : plg.mvfml.Calculus[E]#Expr  
 必須: c.Expr  
        ケース 0 => 新しい c.Neg(genRndExpr(c、レベル + 1))  

次のようなことをしようとすると、同じことが起こります。

val boolCalc = new Calculus(Bool)
val e1: boolCalc.Expr = genRndExpr(boolCalc)

ジェネレーター自体は問題ではありませんが、システムの残りの部分で同様のこと (つまり、微積分インスタンス式の作成と操作) を行う必要があることに注意してください。

私は何か間違ったことをしていますか?私がやりたいことをすることは可能ですか?

この問題に関するヘルプは非常に必要であり、高く評価されています. よろしくお願いします。


Apocalisp から回答を受け取り、試した後。

回答ありがとうございます。ただし、まだいくつかの問題があります。提案された解決策は、関数の署名を次のように変更することでした。

def genRndExpr[E, C <: Calculus[E]](c: C, level: Int): C#Expr

関連するすべての関数 (getRndExpr、getRndVal、および getRndVar) の署名を変更しました。そして、これらの関数を呼び出すすべての場所で同じエラー メッセージが表示され、次のエラー メッセージが表示されました。

エラー: 推論された型引数 [Nothing,C] はメソッド genRndVar に準拠していません
型パラメータ境界 [E,C <: plg.mvfml.Calculus[E]]
        ケース 0 => genRndVar(c)

コンパイラが正しい型を判断できないように見えたので、すべての関数呼び出しを次のように変更しました。

case 0 => new c.Neg(genRndExpr[E,C](c, level+1))

この後、最初の 2 回の関数呼び出し (genRndVal と genRndVar) ではコンパイル エラーは発生しませんでしたが、次の 3 回の呼び出し (genRndExpr への再帰呼び出し) では、関数の戻り値を使用して新しい Expr オブジェクトを作成します。次のエラー:

エラー: タイプが一致しません。
 見つかった: C#Expr
 必須: c.Expr
        ケース 0 => 新しい c.Neg(genRndExpr[E,C](c, level+1))

だから、また行き詰まった。どんな助けでも大歓迎です。

4

2 に答える 2

3

問題は、Scala が と の 2 つの型を統合できないことCalculus[E]#ExprですCalculus[E]#Expr

あなたには同じように見えますよね?さて、ある typeEに対して、それぞれが独自のExpr型を持つ 2 つの異なる計算を行うことができると考えてください。また、この 2 つの表現を混在させたくはありません。

戻り値の型が引数の内部型と同じExpr型になるように、型を制約する必要があります。あなたがしなければならないことはこれです:ExprCalculus

def genRndExpr[E, C <: Calculus[E]](c: C, level: Int): C#Expr
于 2010-04-10T01:02:10.187 に答える
1

Calculusから特定の計算を導き出したくない場合は、Exprをグローバルスコープに移動するか、グローバルスコープを介して参照します。

class Calculus[E] {
    abstract class Expression
    final type Expr = Calculus[E]#Expression

    ... the rest like in your code
}

この質問はまったく同じ問題を指します。

Calculusのサブタイプを作成し、そこでExprを再定義したい場合(ありそうもないことですが)、次のことを行う必要があります。

getRndExprをCalculusクラスに入れるか、getRndExprを派生トレイトに入れます。

 trait CalculusExtensions[E] extends Calculus[E] { 
     def getRndExpr(level: Int) = ...
     ...
 }

その理由については、このスレッドを参照してください。

于 2010-04-10T13:46:15.943 に答える