定义抽象类扩展的函数

时间:2014-06-29 07:04:34

标签: scala abstract-class type-parameter

我在尝试编写一个函数时遇到类型不匹配的问题,该函数将扩展抽象类的对象作为输入(和输出)。

这是我的抽象类:

abstract class Agent {
  type geneType
  var genome: Array[geneType]
}

这是我的功能:

def slice[T <: Agent](parentA: T, parentB: T):(T, T) = {
  val genomeSize = parentA.genome.length

  // Initialize children as identical to parents at first. 
  val childA = parentA
  val childB = parentB

  // the value 'index' is sampled randomly between 0 and 
  // the length of the genome, less 1.  
  // This code omitted for simplicity. 
  val index;
  val pAslice1 = parentA.genome.slice(0, index + 1)
  val pBslice1 = parentB.genome.slice(index + 1, genomeSize)
  val genomeA = Array.concat(pAslice1, pBslice1)
  childA.genome = genomeA

  // And similary for childB. 
  // ...
  // ...

  return (childA, childB)
}

我收到错误(顺便说一下,我用sbt运行),如下所示:

[error] ..........  type mismatch;
[error]  found   : Array[parentA.geneType]
[error]  required: Array[T#geneType]

我不确定问题是什么,因为我是抽象类,泛型类型参数化的新手,也可能是我不知道的其他相关概念。

1 个答案:

答案 0 :(得分:2)

在你的构造中,parentAparentB很可能是不同的类型,T只给你一个上限(它们必须至少与T一样具体) )。数组在元素类型中是不变的,因此你不能在这里以合理的方式交换元素。

您的代码的第二个问题是您返回T类型的对象,但实际上您正在改变输入参数。你想要变异,然后声明方法的返回类型Unit以使其清晰;或者创建T的新实例并使Agent成为不可变的。这取决于你的性能要求,但我总是首先尝试不可变的变体,因为它更容易推理。

这是可变的变体。请注意,因为数组是JVM上的特殊对象(没有发生类型擦除),所以你需要为它们提供所谓的类标记:

abstract class Agent {
  type geneType
  var genome: Array[geneType]
  implicit def geneTag: reflect.ClassTag[geneType]
}

def slice[A](parentA: Agent { type geneType = A }, 
             parentB: Agent { type geneType = A }): Unit = {
  val genomeSize = parentA.genome.length
  require (parentB.genome.length == genomeSize)
  import parentA.geneTag

  val index    = (math.random * genomeSize + 0.5).toInt
  val (aInit, aTail) = parentA.genome.splitAt(index)
  val (bInit, bTail) = parentB.genome.splitAt(index)
  val genomeA  = Array.concat(aInit, bTail)
  val genomeB  = Array.concat(bInit, aTail)
  parentA.genome = genomeA
  parentB.genome = genomeB
}

在此,您需要parentAparentB分享一个确切定义的基因类型A。您可以定义类型别名以简化指定类型:

type AgentT[A] = Agent { type geneType = A }

def slice[A](parentA: AgentT[A], parentB: AgentT[A]): Unit = ...

要保留父项并创建新子项,最简单的方法是将复制方法添加到Agent类:

abstract class Agent {
  type geneType
  var genome: Array[geneType]
  implicit def geneTag: reflect.ClassTag[geneType]

  def copy(newGenome: Array[geneType]): AgentT[geneType]
}

type AgentT[A] = Agent { type geneType = A }

def slice[A](parentA: AgentT[A], parentB: AgentT[A]): (AgentT[A], AgentT[A]) = {
  val genomeSize = parentA.genome.length
  require (parentB.genome.length == genomeSize)
  import parentA.geneTag

  val index    = (math.random * genomeSize + 0.5).toInt
  val (aInit, aTail) = parentA.genome.splitAt(index)
  val (bInit, bTail) = parentB.genome.splitAt(index)
  val genomeA  = Array.concat(aInit, bTail)
  val genomeB  = Array.concat(bInit, aTail)
  (parentA.copy(genomeA), parentB.copy(genomeB))
}

如果您不需要压缩性能的最后几位,则可以使用Vector而不是Array等不可变集合。

case class Agent[A](genome: Vector[A]) {
  def size = genome.size
}

def slice[A](parentA: Agent[A], parentB: Agent[A]): (Agent[A], Agent[A]) = {
  val genomeSize = parentA.size
  require (parentB.size == genomeSize)

  val index    = (math.random * genomeSize + 0.5).toInt
  val (aInit, aTail) = parentA.genome.splitAt(index)
  val (bInit, bTail) = parentB.genome.splitAt(index)
  val genomeA  = aInit ++ bTail
  val genomeB  = bInit ++ aTail
  (parentA.copy(genomeA), parentB.copy(genomeB))
}