Scala 为抽象类的扩展定义函数

Scala 为抽象类的扩展定义函数,scala,abstract-class,type-parameter,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 // Initiali

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

这是我的抽象类:

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)
}

我不确定问题出在哪里,因为我不熟悉抽象类、泛型类型参数化,可能还不知道其他相关概念的名称

在你的结构中,
parentA
parentB
很可能是不同的类型,
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
}
这里您需要
parentA
parentB
共享一个精确定义的基因类型
A
。可以定义类型别名以简化指定该类型:

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

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

要保留父对象并创建新的子对象,最简单的方法是将copy方法添加到
代理
类:

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))
}

需要注意的一点是,在这里执行
parentA.genome.slice(0,index+1)
时,返回类型是
Array[parentA.type#geneType]
,而您只希望返回
Array[geneType]
。但是我不知道为什么,可能是
t
一个抽象类的子类型使得类型绑定到类实例。谢谢,伙计。我想我已经得到了您建议的不变解决方案(仍然使用数组)。我该如何部分应用这个函数?你说的“部分应用”是什么意思?我的意思是得到一个“部分应用的函数”文本,就像你写的那样:val slicer=slice(u,u)。再加上类型参数,例如,
slice[Int]
还有一个问题。我选择了你建议的第二种方法。如果将抽象类定义和“slice”函数的定义放在不同的文件中,那么定义类型别名的最合适位置是哪里?如果在抽象类中定义类型,则会出现找不到该类型的错误。如何将此类型别名导入定义切片函数的文件中?
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))
}