2016-07-10 16 views
0

オーバーライドメソッドで具体的な型を返す簡単な方法はありますか?具体的な実装のインスタンスを作成するのはどうですか?具体的なクラスに実装されたチェーンメソッドを呼び出すので、正しい型も返されます。私は(https://stackoverflow.com/a/14905650に基づいて)解決策を持っていますが、私はこれらのことがより簡単であると感じています。Scalaの型パラメータと継承

多くの類似の質問がありますが、みんなのケースは少し異なりますので、別の例を挙げます(https://github.com/valdanylchuk/saiml/tree/master/src/main/scala/saiml/gaから短縮)。可能であれば、コードブロック全体があなたの提案された変更でコンパイルされているかどうかを確認してください。微妙なカスケード効果があるので、返信してください。私は、 "好奇心をそそるテンプレートのパターン"でこの作業をすることはできませんでした。

import scala.reflect.ClassTag 
import scala.util.Random 

abstract class Individual(val genome: String) { 
    type Self 
    def this() = this("") // please override with a random constructor 
    def crossover(that: Individual): Self 
} 
class HelloGenetic(override val genome: String) extends Individual { 
    type Self = HelloGenetic 
    def this() = this(Random.alphanumeric.take("Hello, World!".length).mkString) 
    override def crossover(that: Individual): HelloGenetic = { 
    val newGenome = this.genome.substring(0, 6) + that.genome.substring(6) 
    new HelloGenetic(newGenome) 
    } 
} 
class Population[A <: Individual {type Self = A} :ClassTag](val size: Int, 
    tournamentSize: Int, givenIndividuals: Option[Vector[A]] = None) { 
    val individuals: Vector[A] = givenIndividuals getOrElse 
    Vector.tabulate(size)(_ => implicitly[ClassTag[A]].runtimeClass.newInstance.asInstanceOf[A]) 
    def tournamentSelect(): A = individuals.head // not really, skipped 
    def evolve: Population[A] = { 
    val nextGen = (0 until size).map { _ => 
     val parent1: A = tournamentSelect() 
     val parent2: A = tournamentSelect() 
     val child: A = parent1.crossover(parent2) 
     child 
    }.toVector 
    new Population(size, tournamentSize, Some(nextGen)) 
    } 
} 
class Genetic[A <: Individual {type Self = A} :ClassTag](populationSize: Int, tournamentSize: Int) { 
    def optimize(maxGen: Int, maxMillis: Long): Individual = { 
    val first = new Population[A](populationSize, tournamentSize) 
    val optPop = (0 until maxGen).foldLeft(first) { (pop, _) => pop.evolve } 
    optPop.individuals.head 
    } 
} 

答えて

1

CRTPバージョンは

abstract class Individual[A <: Individual[A]](val genome: String) { 
    def this() = this("") // please override with a random constructor 

    def crossover(that: A): A 
} 
class HelloGenetic(override val genome: String) extends Individual[HelloGenetic] { 
    def this() = this(Random.alphanumeric.take("Hello, World!".length).mkString) 
    override def crossover(that: HelloGenetic): HelloGenetic = { 
    val newGenome = this.genome.substring(0, 6) + that.genome.substring(6) 
    new HelloGenetic(newGenome) 
    } 
} 
class Population[A <: Individual[A] :ClassTag](val size: Int, 
    tournamentSize: Int, givenIndividuals: Option[Vector[A]] = None) { 
    val individuals: Vector[A] = givenIndividuals getOrElse 
    Vector.tabulate(size)(_ => implicitly[ClassTag[A]].runtimeClass.newInstance.asInstanceOf[A]) 
    def tournamentSelect(): A = individuals.head // not really, skipped 

    def evolve: Population[A] = { 
    val nextGen = (0 until size).map { _ => 
     val parent1: A = tournamentSelect() 
     val parent2: A = tournamentSelect() 
     val child: A = parent1.crossover(parent2) 
     child 
    }.toVector 
    new Population(size, tournamentSize, Some(nextGen)) 
    } 
} 
class Genetic[A <: Individual[A] :ClassTag](populationSize: Int, tournamentSize: Int) { 
    def optimize(maxGen: Int, maxMillis: Long): Individual[A] = { 
    val first = new Population[A](populationSize, tournamentSize) 
    val optPop = (0 until maxGen).foldLeft(first) { (pop, _) => pop.evolve } 
    optPop.individuals.head 
    } 
} 

compilesあります。インスタンスを作成するために、私はちょうど渡す機能をお勧めしたい:

class Population[A <: Individual[A]](val size: Int, 
    tournamentSize: Int, makeIndividual:() => A, givenIndividuals: Option[Vector[A]] = None) { 
    val individuals: Vector[A] = givenIndividuals getOrElse 
    Vector.fill(size)(makeIndividual()) 
    ... 
} 

あなたが暗黙のうちにそれらを渡したい場合は、あなたが簡単にそれを行うことができます。

trait IndividualFactory[A] { 
    def apply(): A 
} 

class HelloGenetic ... // remove def this() 
object HelloGenetic { 
    implicit val factory: IndividualFactory[HelloGenetic] = new IndividualFactory[HelloGenetic] { 
    def apply() = new HelloGenetic(Random.alphanumeric.take("Hello, World!".length).mkString) 
    } 
} 
class Population[A <: Individual[A]](val size: Int, 
    tournamentSize: Int, givenIndividuals: Option[Vector[A]] = None) 
    (implicit factory: IndividualFactory[A]) { 
    val individuals: Vector[A] = givenIndividuals getOrElse 
    Vector.fill(size)(factory()) 
    ... 
} 
+0

ありがとうございました! CRTPのバージョンは少しきれいに見えます。インスタンスを作成するために、私はその関数を渡すことを試みます。 HelloGeneticクラスだけでなく、コンパニオンオブジェクトを実装するライブラリユーザーを必要とせずに動作させることができるかどうかはわかりません。私は、私の内部実装の反映を犠牲にしても、インターフェイスを簡単にしたいと思っています。私は完全なプロジェクトでこれを試し、それが間違いなく資格を与えられているにもかかわらず、これを受け入れられた答えとしてマークする前に、より多くの提案があるかどうかを確認します。 –

+0

はい、CRTPはこのプロジェクトのすべてのケースで完璧に動作します。以前に試してみたところ、私は一貫していなかった。 –

関連する問題