在Scala中键入参数和继承

时间:2016-07-10 22:54:55

标签: scala inheritance type-parameter

是否有一种简单的方法可以在覆盖方法中返回具体类型?那么创建一个具体实现的实例呢?并调用在具体类中实现的链式方法,因此它们也会返回正确的类型?我有一个解决方案(基于https://stackoverflow.com/a/14905650),但我觉得这些事情应该更简单。

有许多类似的问题,但每个人的情况都有一点不同,所以这是另一个例子(缩写为https://github.com/valdanylchuk/saiml/tree/master/src/main/scala/saiml/ga)。在回复时,如果可能,请检查整个代码块是否与您建议的更改进行编译,因为存在微妙的级联效果。我无法使用#34;奇怪的重复模板模式",(不是我发现它更好)。

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 个答案:

答案 0 :(得分: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())
  ...
}