2

同じスーパータイプのクラスがいくつかあります。したがって、このクラスはすべて同じメソッドをオーバーライドする必要があります。これで、メソッドを呼び出して、共通のスーパータイプのオブジェクトとしてコミットできます。ただし、コミットされた各タイプに反応することが常に役立つとは限らないため、例外がスローされます。最初に、この動作を次のように解決しようとしました:

def operation(s: SuperType) = s match {
  case t: SubType1 => ...
  case t: SubType2 => ...
  case _ => ...
}

サブタイプが多いため、(各メソッドおよび各クラスで) 多くのコードが必要になるため、この問題を で解決しようとしましたtraits。各特性は 1 つの型のみをテストし、オブジェクトをスタックの上位のメソッドに転送する必要があります。以下のコードは、私がそれをどのように想像するかを説明しています。ただし、コンパイラは型を分解できないため、これは機能しません。もう 1 つの問題は、各動作クラスでクラスの各属性を宣言する必要があることです。

object TraitWithTest {
  def main(args: Array[String]) {
    val e1 = Even(2, 4)
    val e2 = Even(1, 3)
    val o1 = Odd(1.25, 3.75)
    val o2 = Odd(7.25, 9.25)
    val a1 = All(5.5)
    val a2 = All(3.5)

    println("e1 + e2: " + (e1 + e2))
    println("o1 + o2: " + (o1 + o2))
    try { println("e1 + o2: " + (e1 + o2)) } catch { case e => println(e) }
    println("o1 + e2: " + (o1 + e2))
    println("a1 + e1: " + (a1 + e2))
  }
}

abstract class Num {
  def +(n: Num): Num
}

trait OddBehaviour extends Num {
  val e1, e2: Int // here I don't want to declare all attributes
  val a1: Double
  abstract override def +(n: Num) = n match {
    case o: Odd => throw new UnsupportedOperationException("Even#+(Odd)")
    case _ => super.+(n)
  }
}

trait EvenBehaviour extends Num {
  val o1, o2: Double
  val a1: Double
  abstract override def +(n: Num) = n match {
    case e: Even => Odd(o1 + e.e1, o2 + e.e2)
    case _ => super.+(n)
  }
}

trait AllBehaviour extends Num {
  val o1, o2: Double
  val e1, e2: Int
  abstract override def +(n: Num) = n match {
    case a: All => Odd(o1 + a.a1, o2 + a.a1)
    case _ => super.+(n)
  }
}

object Even {
  def apply(e1: Int, e2: Int) = new Even(e1, e2) with OddBehaviour with AllBehaviour
}

abstract case class Even(e1: Int, e2: Int) extends Num {
  override def +(n: Num) = n match {
    case c: Even => Even(e1 + c.e1, e2 + c.e2)
    case _ => throw new IllegalArgumentException
  }
}

object Odd {
  def apply(o1: Double, o2: Double) = new Odd(o1, o2) with EvenBehaviour with AllBehaviour
}

abstract case class Odd(o1: Double, o2: Double) extends Num {
  override def +(n: Num) = n match {
    case o: Odd => Odd(o1 + o.o1, o2 + o.o2)
    case _ => throw new IllegalArgumentException
  }
}

object All {
  def apply(a1: Double) = new All(a1) with EvenBehaviour with OddBehaviour
}

abstract case class All(a1: Double) extends Num {
  override def +(n: Num) = n match {
    case a: All => All(a1 + a.a1)
    case _ => throw new IllegalArgumentException
  }
}

特性を使用してコード行を減らすことができるかどうか誰か教えてもらえますか? または、現在使用しているすべてのソリューションが最適ですか?

編集:

あなたの助けを借りて、半分有効な解決策を見つけました。私の主な問題は、Scala 機能を使用してコード行を削減しようとしたことです。そのため、最も簡単な方法を見落としていました。それは、コードをアウトソーシングすることです! オブジェクトの組み合わせをチェックする新しいオブジェクトを作成するだけです。オブジェクト自体は、独自の型のみを処理します。

これはコードです:

final object TraitWithTest {
  def main(args: Array[String]) {
    import traitwith.operations._
    val e1 = Even(2, 4)
    val e2 = Even(1, 3)
    val o1 = Odd(1.25, 3.75)
    val o2 = Odd(7.25, 9.25)
    val a1 = All(5.5)
    val a2 = All(3.5)

    val n1 = NumHolder(o1)
    val n2 = NumHolder(a1)

    println("e1 + e2: " + add(e1, e2))
    println("o1 + o2: " + add(o1, o2))
    try { println("e1 + o2: " + add(e1, o2)) } catch { case e => println(e) }
    println("o1 + e2: " + add(o1, e2))
    try { println("a1 + e2: " + add(a1, e2)) } catch { case e => println(e) }
    println("n1 + n2: " + add(n1, n2))
  }
}

final object operations {
  def add(a: Num, b: Num) = a -> b match {
    case (a1: Odd, b1: Odd) => a1 + b1
    case (a1: Odd, b1: Even) => Odd(a1.x + b1.x, a1.y + b1.y)
    case (a1: Odd, b1: All) => Odd(a1.x + b1.x, a1.y + b1.x)
    case (a1: Even, b1: Even) => a1 + b1
    case (a1: All, b1: All) => a1 + b1
    case _ => error("can't add " + b + " to " + a)
  }
}

abstract class Num {
  type A <: Num
  def +(a: A): A
}

final case class Odd(x: Double, y: Double) extends Num {
  override type A = Odd
  override def +(a: Odd) = Odd(x + a.x, y + a.y)
}

final case class Even(x: Int, y: Int) extends Num {
  override type A = Even
  override def +(a: Even) = Even(x + a.x, y + a.y)
}

final case class All(x: Double) extends Num {
  override type A = All
  override def +(a: All) = All(x + a.x)
}

final case class NumHolder(x: Num) extends Num {
  override type A = NumHolder
  override def +(a: NumHolder) = NumHolder(x + a.x)
}

コードを少し拡張して、 object を挿入しましたNumHolder。ここで、小さな欠陥が 1 つだけあります。NumHolder では、追加メソッドでコンパイル エラーが発生しない限り、スーパータイプをコミットできません。type-keyword の代わりに Generics を使用しようとしましたが、常に型を Num に設定する必要があるため (オブジェクト操作でも)、これは不便です。

この小さなコンパイル エラーを解決するにはどうすればよいですか?

4

6 に答える 6

5

あなたの問題は、オブジェクト指向ではない設計で、クラスや継承などのオブジェクト指向の機能を使用しようとしていることです。

OOP の要点は、クラスとは何かを内省しないことです。代わりに、ポリモーフィズムを使用して結果を達成してください。オブジェクト指向がどのように機能するかを説明しているこの論文が特に気に入っていますが、この点に関してリソースが不足しているわけではありません。

編集

たとえば、提供されたコードは、動作しないものを除いて、大まかに次のように変換されます (提供されたコードは、それらのために正確にコンパイルされません)。

abstract class Num {
  def +(n: Num): Num
  def plus(n1: Int, n2: Int): Num
  def plus(n1: Double, n2: Double): Num
  def plus(n: Double): Num
}

case class Even(e1: Int, e2: Int) extends Num {
  override def +(n: Num) = n.plus(e1, e2)
  override def plus(n1: Int, n2: Int) = Even(e1 + n1, e2 + n2)
  override def plus(n1: Double, n2: Double) = Odd(n1 + e1, n2 + e2)
  // the code provided references o1 and o2, which are not defined anywhere for Even
  // so I'm providing an alternate version
  override def plus(n: Double) = Odd(n + e1, n + e2)
}

case class Odd(o1: Double, o2: Double) extends Num {
  override def +(n: Num) = n.plus(o1, o2)
  override def plus(n1: Int, n2: Int) = throw new UnsupportedOperationException("Even#+(Odd)")
  override def plus(n1: Double, n2: Double) = Odd(o1 + n1, o2 + n2)
  override def plus(n: Double) = throw new UnsupportedOperationException("Even#+(Odd)")
}

case class All(a1: Double) extends Num {
  override def +(n: Num) = n.plus(a1)
  // the code provided references o1 and o2, which are not defined anywhere for All
  // so I'm providing an alternate version
  override def plus(n1: Int, n2: Int) = Odd(a1 + n1, a1 + n2)
  override def plus(n1: Double, n2: Double) = Odd(n1 + a1, n2 + a1)
  override def plus(n: Double) = All(a1 + n)
}

ビジター パターンを使用してさらに改善できるように思えますが、型マッチングが通常行うのと同じ問題を対象としていることを考えると、これは理にかなっています。

于 2010-10-30T12:45:46.957 に答える
3

実行時まで型がわからない場合の代替ソリューション:

sealed trait Num
case class Even(x:Int, y:Int) extends Num
case class Odd(x:Double, y:Double) extends Num
case class All(x:Double) extends Num

object operations {
  def add(a: Num, b: Num) : Num = (a,b) match {
    case (a1:Even, b1:Even) => Even(a1.x+b1.x, a1.y+b1.y)
    case (a1:Odd, b1:Odd) => Odd(a1.x+b1.x, a1.y+b1.y)
    case (a1:Odd, b1:Even) => Odd(a1.x+b1.x, a1.y+b1.y)
    case (a1:All, b1:All) => All(a1.x, b1.x)
    case _ => error("can't add " + a + " to " + b)
  }
}

ここでの秘訣は、最初に両方のパラメーターをタプルにラップすることです。これにより、パターンマッチングを行う単一のオブジェクトができます。

アップデート

編集後; 抽象型はどこにも必要ないようです。マーカー特性としてA残しNum、各サブクラスで+メソッドを個別に定義してみませんか?

sealed abstract trait Num

case class Odd(x: Double, y: Double) extends Num {
  def +(a: Odd) = Odd(x + a.x, y + a.y)
}

final case class Even(x: Int, y: Int) extends Num {
  def +(a: Even) = Even(x + a.x, y + a.y)
}

final case class All(x: Double) extends Num {
  def +(a: All) = All(x + a.x)
}

final case class NumHolder(x: Num) extends Num {
  def +(a: NumHolder) = NumHolder(x + a.x)
}
于 2010-10-30T19:12:13.820 に答える
3

特定の広告を誤って引用するには: そのための型クラスがあります...

Scala はすでに を介して「数値」を介したアドホック ポリモーフィズムをサポートしNumericます。 /Numeric.html

しかし、この偶数/奇数/すべてのスキームが実際に行っていることであり、単なる不自然な例ではない場合、いつでも独自の型クラスを展開できます!


それを呼びましょうAddable

case class Even(x:Int, y:Int)
case class Odd(x:Double, y:Double)
case class All(x:Double)

abstract class Addable[A, B] {
  def add(a: A, b: B): A
}

implicit object EvenCanAddEven extends Addable[Even, Even] {
  def add(a:Even, b:Even) = Even(a.x+b.x, a.y+b.y)
}

implicit object OddCanAddOdd extends Addable[Odd, Odd] {
  def add(a:Odd, b:Odd) = Odd(a.x+b.x, a.y+b.y)
}

implicit object OddCanAddEven extends Addable[Odd, Even] {
  def add(a:Odd, b:Even) = Odd(a.x+b.x, a.y+b.y)
}

implicit object AllCanAddAll extends Addable[All, All] {
  def add(a:All, b:All) = All(a.x+b.x)
}

def add[A,B](a:A, b:B)(implicit tc: Addable[A,B]) =
  tc.add(a, b)


val e1 = Even(2, 4)
val e2 = Even(1, 3)
val o1 = Odd(1.25, 3.75)
val o2 = Odd(7.25, 9.25)
val a1 = All(5.5)
val a2 = All(3.5)

println("e1 + e2: " + add(e1, e2))
println("o1 + o2: " + add(o1, o2))
println("e1 + o2: " + add(e1, o2)) //compiler should fail this line
println("o1 + e2: " + add(o1, e2))
println("a1 + e1: " + add(a1, e2))

免責事項: 私は実際にコードをテストしていません。このマシンには (まだ) Scala がインストールされていません。

于 2010-10-30T16:34:12.400 に答える
2

私はあなたの問題を解決できるかどうかわかりませんが、それについて考えている間、私は少なくともあなたの例をコンパイルして動作させることを試みました、それはうまくいけば少なくともいくらか役立つでしょう。

toStringインスタンス化と式の結果を確認できるように、メソッドの形でノイズを追加しました。

abstract class Num(val a: Double, val b: Double) {
  def +(that: Num): Num
  override def toString = (<z>Num({a}, {b})</z> text)
}

「最後の手段」クラスはAll、マッチングをスムーズにするためのケースクラスである必要がありますが、実際のケースクラスとして継承されるため、うまく機能しません。applyおよびunapplyメソッドを持つコンパニオンオブジェクトがそれを解決します。

は同様の用語を処理できますが、それらの用語は同時に密かにsであるため、および用語Allを処理しようとはしません。EvenOddAll

class All(override val a: Double, override val b: Double) extends Num(a, b) {
  def +(that: Num): Num = that match {
    case All(n) => All(this.a + n)
    case _      => error("I don't know this subtype")
  }
  override def toString = (<z>All({a})</z> text)
}
object All {
  def apply(num: Double) = new All(num, num)
  def unapply(num: All) = Some(num.a)
}

Evenこれで、 sとsの動作方法Oddを特性にまとめることができますが、この例では必要ありません。そうしないと継承が単純化されますが、例のポイントに反する可能性があります。わかりません。

Anは処理方法と用語Evenを知っていますが、他のすべてをそのスーパークラスに渡します。繰り返しになりますが、これはマッチングを目的とした偽のケースクラスです。EvenOdd

class Even(override val a: Double, override val b: Double) extends All(a, b) {
  override def +(that: Num): Num = that match {
    case Even(a, b) => Even(this.a + a, this.b + b)
    case Odd(a, b)  => Odd(this.a + a, this.b + b)
    case x => super.+(x)
  }
  override def toString = (<z>Even({a}, {b})</z> text)
}
object Even {
  def apply(a: Double, b: Double) = new Even(a, b)
  def unapply(num: Even) = Some((num.a, num.b))
}

Anは用語のOdd処理方法を知っていますがEven、用語の処理を拒否しOddます(これをあなたの例から変更して、駄洒落を弱くしました。どういたしまして)。

class Odd(override val a: Double, override val b: Double) extends All(a, b) {
  override def +(that: Num): Num = that match {
    case Even(a, b) => Odd(this.a + a, this.b + b)
    case Odd(a, b)  => error("Adding two Odds is an odd thing to do")
    case x => super.+(x)
  }
  override def toString = (<z>Odd({a}, {b})</z> text)
}
object Odd {
  def apply(a: Double, b: Double) = new Odd(a, b)
  def unapply(num: Odd) = Some((num.a, num.b))
}

OK、試してみましょう。

object Try {
  def main(args: Array[String]) {
    val e1 = Even(2, 4)
    val e2 = Even(1, 3)
    val o1 = Odd(1.25, 3.75)
    val o2 = Odd(7.25, 9.25)
    val a1 = All(5.5)
    val a2 = All(3.5)

    println("e1 + e2: " + (e1 + e2))
    println("e1 + o2: " + (e1 + o2))
    try { println("o1 + o2: " + (o1 + o2)) } catch { case e => println(e) }
    println("o1 + e2: " + (o1 + e2))
    println("a1 + e1: " + (a1 + e2))
  }
}
于 2010-10-30T21:18:09.300 に答える
0

ケビン・ライトの答えに基づいて、私は今問題を解決しました:

package de.traitwith

import de.traitwith.Operations._
import de.traitwith._

object TraitWithTest {
  def main(args: Array[String]) {
    val e1 = Even(2, 4)
    val e2 = Even(1, 3)
    val o1 = Odd(1.25, 3.75)
    val o2 = Odd(7.25, 9.25)
    val a1 = All(5.5)
    val a2 = All(3.5)
    val n1 = NumHolder(o1)
    val n2 = NumHolder(a1)
    println("e1 + e2: " + add(e1, e2))
    println("o1 + o2: " + add(o1, o2))
    try { println("e1 + o2: " + add(e1, o2)) } catch { case e => println(e) }
    println("o1 + e2: " + add(o1, e2))
    try { println("a1 + e2: " + add(a1, e2)) } catch { case e => println(e) }
    println("n1 + n2: " + add(n1, n2))
    println("o1 + n2: " + add(o1, n2))
  }
}

object Operations {
  def add(a: Num, b: Num): Num = a -> b match {
    case (a1: Odd, b1: Odd) => a1 + b1
    case (a1: Odd, b1: Even) => Odd(a1.x + b1.x, a1.y + b1.y)
    case (a1: Odd, b1: All) => Odd(a1.x + b1.x, a1.y + b1.x)
    case (a1: Odd, b1: NumHolder) => add(a1, b1.x)
    case (a1: Even, b1: Even) => a1 + b1
    case (a1: Even, b1: NumHolder) => add(a1, b1.x)
    case (a1: All, b1: All) => a1 + b1
    case (a1: All, b1: NumHolder) => add(a1, b1.x)
    case (a1: NumHolder, b1: NumHolder) => a1 + b1
    case (a1: NumHolder, b1: Odd)=> add(a1.x, b1)
    case (a1: NumHolder, b1: Even) => add(a1.x, b1)
    case (a1: NumHolder, b1: All) => add(a1.x, b1)
    case _ => error("can't add " + b + " to " + a)
  }
}

abstract class Num

final case class Odd(x: Double, y: Double) extends Num {
  def +(a: Odd) = Odd(x + a.x, y + a.y)
}

final case class Even(x: Int, y: Int) extends Num {
  def +(a: Even) = Even(x + a.x, y + a.y)
}

final case class All(x: Double) extends Num {
  def +(a: All) = All(x + a.x)
}

final case class NumHolder(x: Num) extends Num {
  def +(a: NumHolder) = NumHolder(add(x, a.x))
}

スーパータイプにはこれ以上いくつかのメソッドがありません。同じ日に問題が発生しないことを願っています。クラス内のすべての追加メソッドを削除することは可能ですが、私の実際のアプリケーションではより大きなクラスがあるため、そこにそれらが必要です。

于 2010-11-01T15:53:14.737 に答える
0

ジェネリックが役に立ちそうです。次のようなことを試してください:

class Supertype[A <: Supertype] {
    def operation(s: A) {

    }
}

class Subtype extends SuperType[Subtype] {
    override def operation(s: Subtype) {

    }
}

あなたの問題の説明はあまり明確ではないので、これは推測です...

于 2010-10-30T14:32:44.467 に答える