2

我正在为 Scala 中的 EA(进化算法)项目开发框架。在这一点上,我有一个实现通用 EA 代码的特征,并将诸如基因型转换和适应性测试之类的问题特定代码留给实现此特征的类。但是,由于测试不同的人口选择协议/策略,我不想在实际运行之前完全实现该特征。这给出了代码

trait EAProblem{
// common code ...
   def fitness(ind:Individual):Double
   def selectionStrategy(p: Population): List[(Individual, Double)]
   def nextGeneration(p: Population): Population
}

/* Silly test problem */
abstract class OneMax(logPath: String) extends EAProblem {
  def phenotype(ind:Individual) = {
    ind.genotype
  }
  def fitness(ind: Individual): Double = {
    ind.genotype.size.toFloat / ind.genotype.capacity
  }
}

在运行时选择协议/策略:

object EASelectionStrategyProtocolDemo {
  def main(args: Array[String]) {

    val problem_impl = List[EAProblem](
      // Full replacement
      new OneMax("sigma_strat_full-rep_prot_onemax.log.dat") {
        def selectionStrategy(p: Population): List[(Individual, Double)] =
          SelectionStrategies.sigmaScalingMatingSelection(p)
        def nextGeneration(p: Population): Population = SelectionProtocols.fullReplacement(p)
      },
      new OneMax("boltz_strat_full-rep_prot_onemax.log.dat") {
        def selectionStrategy(p: Population): List[(Individual, Double)] =
          SelectionStrategies.boltzmannSelection(p)
        def nextGeneration(p: Population): Population = SelectionProtocols.fullReplacement(p)
      })
    for(problem <- problem_impl)
       new Simulator(problem)
}

SelectionStrategies/SelectionProtocols 对象包含对 EAProblem 中其他代码的引用的clusures。

我现在想要的是使用反射(或其他一些机制)实例化其他抽象类的方法,比如 OneMax(我有很多)。伪代码:

val listOfClassNames = List("OneMax", "classA", "classB", ...)
for(className <- listOfClassNames){
    class_sigma = Class.forname(className)
    /*
    Implement class_class with this code and instantiate it
    def selectionStrategy(p: Population): List[(Individual, Double)] =
          SelectionStrategies.sigmaScalingMatingSelection(p)
    def nextGeneration(p: Population): Population = SelectionProtocols.fullReplacement(p)
    */
    class_boltz = Class.forname(className)
    /*
    Implement class_boltz with this code and instantiate it
    def selectionStrategy(p: Population): List[(Individual, Double)] =
          SelectionStrategies.boltzmannSelection(p)
    def nextGeneration(p: Population): Population = SelectionProtocols.fullReplacement(p)
    */
}
4

3 回答 3

1

我不知道你是否可以通过反射来实例化它,同时定义抽象方法。或者,至少,不容易。

你为什么不把这些方法变成函数呢?我会这样做的方式是这样的:

private var _selectionStrategy: Option[Population => List[(Individual, Double)]] = None
def selectionStrategy(p: Population) = _selectionStrategy.getOrElse(error("Uninitialized selection strategy!"))(p)
def setSelectionStrategy(f: Population => List[(Individual, Double)]) = if (_selectionStrategy.isEmpty)
  _selectionStrategy = f
else
  error("Selection strategy already initialized!")

// Same thing for nextGeneration

当然,OneMax那时不会是抽象的。实际上,这是重点。然后,您使用反射来创建一个新的实例OneMax,这是相当直接的,并使用setSelectionStrategyandsetNextGeneration来设置函数。

于 2010-02-01T18:21:32.823 回答
1
  1. 你不能实例化抽象类
  2. 你不需要抽象类

Fitness、selectionStrategy、nextGeneration - 都是“独立”变量。因此,将它们捆绑在一个界面中与问题的本质背道而驰。尝试这个:

type Fitness = Individual => Double
type SelectionStrategy = Population = > List[(Individual, Double)]
type NextGeneration = Population => Population

case class EAProblem(
  fitness: Fitness, 
  selectionStrategy: SelectionStrategy, 
  nextGeneration: NextGeneration) { /* common code */ }

val fitnesses = List(OneMax, classA, classB, ...)
val strategies = List(
  SelectionStrategies.sigmaScalingMatingSelection, 
  SelectionStrategies.boltzmannSelection)

fitnesses.map ( fitness => 
  strategies.map ( strategy =>
    EAProblem(fitness, strategy, SelectionProtocols.fullReplacement)))

编辑:您可以使用 CGLib 等实例化抽象类...

于 2010-02-01T20:09:00.517 回答
0

如果您真的想以这种方式使用反射(使用运行时代码编译),我认为您最好使用 Python 之类的语言。但我不认为你真的想以这种方式使用反射。

我认为你最好的选择是让第二类而不是特征包含执行健身测量的例程。例如,

abstract class Selector {
  def fitness(ind: Individual): Double
  def name: String
}
class Throughput extends Selector {
  def fitness(ind: Individual) = ind.fractionCorrect/ind.computeTime
  def name = "Throughput"
}

然后你可以

val selectors = List(new Throughput, new ...)
val userInputMap = List.map( t => (t.name , t) ).toMap

并按名称查找正确的选择器。

然后,您可以让 OneMax(和其他人)将选择器作为构造函数参数,您可以通过 userInputMap 从字符串中提供该参数。

于 2010-02-01T18:45:32.180 回答