質問
同じスーパータイプのクラスがいくつかあります。したがって、このクラスはすべて同じ方法をオーバーライドする必要があります。これで、メソッドを呼び出して、一般的なスーパータイプのオブジェクトをコミットできます。しかし、コミットされた各タイプに反応することは常に有用ではないため、例外がスローされます。最初に私はこのようなこの動作を解決しようとしました:
def operation(s: SuperType) = s match {
case t: SubType1 => ...
case t: SubType2 => ...
case _ => ...
}
多くのサブタイプのため、これは多くのコード(各方法と各クラスで)になり、この問題を解決しようとしました traits
. 。各特性は、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 Featuresを使用してコードの行を削減しようとしたことでした。だから私は最も簡単な方法を見落としていました:コードのアウトソーシング!オブジェクトの結合をチェックする新しいオブジェクトを作成するだけです。オブジェクト自体は、独自のタイプのみを処理します。
これはコードです:
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)
}
コードを少し拡張してオブジェクトを挿入しました NumHolder
. 。さて、小さな欠陥は1つだけです。Numholderでは、Add-Methodでコンパイルエラーを取得せずにスーパータイプをコミットすることはできません。私はタイプキーワードの代わりにジェネリックを使用しようとしましたが、それは常にタイプをnumに設定する必要があるので、それは不快です オペレーション).
この小さなコンパイルエラーを解決するにはどうすればよいですか?
解決 6
ケビン・ライトに基づいています 答え 私は今問題を解決しました:
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))
}
他のヒント
あなたの問題は、オブジェクト指向ではないデザインを使用して、クラスや継承などのオブジェクト指向の機能を使用しようとしていることです。
全体 点 OOPは、クラスとは何かを内省しないということです。代わりに、多型を使用して結果を達成します。私は特に好きです この紙 OOがどのように機能するかを説明する際には、この点でリソースの不足はありません。
編集
たとえば、提供されたコードは、大まかに次のものに変換され、機能しないものを差し引いて(提供されたコードは、それらのために正確にコンパイルされません)。
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)
}
訪問者のパターンでさらに改善できるように見えます。これは、タイプマッチングが通常行うのと同じ問題をターゲットにすることを考えると、理にかなっています。
特定の広告を誤って引用するために:そのためのタイプクラスがあります...
Scalaはすでに「数字」を超えるアドホック多型をサポートしています Numeric
, 、それはおそらくあなたのことです 本当 欲しいです: http://www.scala-lang.org/archives/downloads/distrib/files/nightly/docs/library/scala/math/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をインストールしていません
ランタイムまでタイプがわかっていない場合のための代替ソリューション:
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)
}
私はあなたの問題を解決できるかどうかはわかりませんが、それについて考えている間、私は少なくともあなたの例を編集して作業するためにあなたの例を取得しようとしました。
の形でノイズを追加しました 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
方法はそれを解決します。
an All
同様の用語を処理できますが、処理しようとしません Even
と Odd
これらの条件は密かにあるため、用語 All
同時にs。
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
砂 Odd
S作業は特性に蒸留される可能性がありますが、この例には必要ありません。そうすることは継承を単純化しませんが、例のポイントに反する可能性がありますが、私は知りません。
an Even
処理方法を知っています Even
と Odd
用語ですが、他の人はスーパークラスに渡します。繰り返しますが、それは一致する目的のための偽のケースクラスです。
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))
}
わかりました、それをスピンにしましょう。
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))
}
}
ジェネリックがあなたを助けるかもしれないようです。このようなことを試してみてください:
class Supertype[A <: Supertype] {
def operation(s: A) {
}
}
class Subtype extends SuperType[Subtype] {
override def operation(s: Subtype) {
}
}
あなたの問題の説明はあまり明確ではないので、これは推測です...