2017-08-24 7 views
0

今私はRealVectorクラスとComplexVectorクラスを持っています。それらのロジックはほぼ同じですので、それらを1つのVectorクラスに結合したいと思います。 RealVectorList[Double]であり、ComplexVectorList[ComplexNumber]であり、ComplexNumberは私が作成したケースクラスです。2つの異なる型を許可するために、どのように私のケースクラスコンストラクタをオーバーロードするのですか?

私のcase class Vectorは、Listのいずれかのタイプを受け入れます。ほとんどのメソッドのコードは同一ですが、Listタイプに応じて、DoubleまたはComplexNumberを返すことがあります。このインスタンスでは、ケースクラスを使用することも正しいですか、または通常のクラスを使用する必要がありますか?

編集:私の現在のコード

trait VectorElement[A] 
implicit object RealVectorElement extends VectorElement[Double] 
implicit object ComplexVectorElement extends VectorElement[ComplexNumber] 

case class MyVector[A: VectorElement](components: List[A]) { 
    def +(that:MyVector[A]):MyVector[A] = { 
     if (this.dimension != that.dimension) throw new Exception("Cannot add MyVectors of different dimensions."); 
     new MyVector((this.components zip that.components).map(c => c._1 + c._2)); 
    } 

    def -(that:MyVector[A]):MyVector[A] = { 
     if (this.dimension != that.dimension) throw new Exception("Cannot subtract MyVectors of different dimensions."); 
     new MyVector((this.components zip that.components).map(c => c._1 - c._2)); // ERROR HERE: error: value - is not a member of type parameter A 
    } 
    ... 
} 
+0

"trait"、genericity、typeclassなどのさまざまな優れたソリューションになる可能性があるオーバーロードではなく、建設的な回答を得るには余りにも幅がありません... https://stackoverflow.com/help/how-to-ask – cchantep

+0

補助コンストラクタがプライマリコンストラクタを呼び出す必要があり、メソッドがどのコンストラクタが使用されたのかを判断することが難しくなる可能性があります。 – jwvh

答えて

1

あなたは、より高度なScalaの型システムの機能の一部を使用して試みることができる:

object Types { 
    trait inv[-A] {} 
    type Or[A, B] = { 
    type check[X] = (inv[A] with inv[B]) <:< inv[X] 
    } 
} 

case class Vector[U : (Double Or Int)#check](list: List[U]) { 
    def first(): U = list.head 
} 

私はここDoubleIntを使用しましたが、どのようなタイプが可能中古。使い方は簡単です:あなたは1つだけのタイプは、ケースにジェネリックを使用することができますしたい場合は

println(Vector(List(1.0, 2.0, 3.0)).first()) // prints a Double 
println(Vector(List(1, 2, 3)).first()) // prints an Int 
//Vector(List("String")).first() // won't compile 
2

最もなじみの方法は、あなたの2つのVectorクラス

abstract class Vector[A](elements: List[A]){ 
    //common code 
} 
case class RealVector(elements: List[Double]) extends Vector[Double](elements) 
case class ComplexVector(elements: List[ComplexNumber]) extends Vector[ComplexNumber](elements) 

の共通スーパータイプを作成することですクラスのでcase class Vector[A](values: List[A])作品。

これで任意の種類のリストが許可されるので、それを絞り込むことができます。カスタムタイプを扱う場合は、特性の共通のスーパータイプを使用できますが、Doubleが組み込まれており、変更できません。

私たちができることは、型付きを使用することです。これは、スケーラで可能な柔軟性のあるポリモズムです。私たちは、特性を持つ型クラスを定義することができます。

trait VectorElement[A] 

私たちがやりたいと思うタイプをマークするのに使いたいだけなら、必要な共通の機能もここに入れることができます。

我々は、我々が利用可能VectorElementインスタンスでのみのタイプに私たちのジェネリック型を制限

case class Vector[A: VectorElement](values: List[A]) 

に我々の場合のクラス定義を変更する場合。上記のコードは、我々は今我々がいる限り、それらの2つの暗黙オブジェクトがスコープ内にあると私たちは私たちのVectorクラスでこれらの型を使用することができ、今

implicit object RealVectorElement extends VectorElement[Double] 
implicit object ComplexVectorElement extends VectorElement[ComplexNumber] 

を必要とするタイプのインスタンスを作成することができcase class Vector[A](values: List[A])(implicit ev: VectorElement[A])

ためのシンタックスシュガーですが、他にはありません。


いくつかの無関係な提案:

Vectorはすでに常に自動的にインポートされ、標準ライブラリのクラスで、これは問題

Listを引き起こす可能性は、おそらくのための最高のコレクション型ではありませんしましたこれは、要素にアクセスするためにコレクションをトラバースする必要があるためです。おそらく、より一般的なものを選択したいと思うかもしれません。おそらく、良いインデックスアクセスが必要でしょう。 Listの代わりにIndexedSeqをコレクションの種類として使用する場合は、ArrayまたはVector(標準ライブラリのもの)のようなインデックスベースのランダムアクセスが良好なコレクションで作業していることを確認できます。

+0

これを実行した後、エラー:値 - 型パラメータAのメンバーではありませんが、 'ComplexNumber'に' -'がオーバーライド/定義され、 'Double'に対して暗黙的に変換されています。どんな考え?そして、IndexedSeqの頭に感謝します。不変なものが欲しかったので、Arrayを使わなかったのです。 – Mike

+0

@Mikeあなたのコードを見る必要があります。あなたはどこの価値を呼びそそうとしていますか?私はあなたが 'A '型でそれを呼び出そうとしているからだと思う。 2つは関連していないので、あなたは 'A'型のComplexNumberメソッドを呼び出すことができません。 typeclassの中に必要なメソッドを定義する必要があります。 – puhlen

+0

私のコードを編集として追加しました。私はあなたが私の問題について正しいと思います。私は自分のメソッドを 'ComplexNumber'メソッドで使用することはできません。なぜなら、自分のメソッドを自分のtypeclassと' ComplexNumber'で定義したくないからです。 – Mike

関連する問題