在 Scala 中通过反射实现/实例化抽象类

发布于 2024-08-20 13:34:42 字数 2401 浏览 7 评论 0原文

我正在为 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)
    */
}

I'm working on a framework for a EA (evolutionary alg) project in Scala. In this i have a trait that implements common EA-code and leaves problem spesific code like genotype convertion and fitness testing to classes that implement this trait. However, I don't want to fully implement the trait before it is actually run because of testing different population selection protocols/strategies. This gives the code

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

At runtime the protocol/strategy is choosen:

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

The SelectionStrategies/SelectionProtocols objects contains clusures with references to other code in EAProblem.

What I want now is some way to instantiate other abstract classes like OneMax (I have many of them) using reflection (or some other mechanism). Pseudocode:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

依 靠 2024-08-27 13:34:42

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

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

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 实例(这相当简单),并使用 setSelectionStrategysetNextGeneration 设置函数。

I don't know if you can instantiate it by reflection while, at the same time, defining the abstract methods. Or, at least, not easily.

Why don't you make these methods into functions? The way I would go about it is something like this:

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

Of course, OneMax wouldn't be abstract then. Which, actually, is kind of the point. You then use reflection to create a new instance of OneMax, which is reasonably straight-forward, and use setSelectionStrategy and setNextGeneration to set the functions.

辞慾 2024-08-27 13:34:42
  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 等实例化抽象类...

  1. you cannot instantiate abstract class
  2. you don't need abstract class

fitness, selectionStrategy, nextGeneration - are all "independent" variables. Thus tying them togather in one interface is going against the nature of the problem. Try this:

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

Edit: you can instantiate abstract class ... with CGLib or such

苯莒 2024-08-27 13:34:42

如果你真的想以这种方式使用反射(通过运行时代码编译),我认为你最好使用像 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 从字符串中提供该参数。

If you really want to use reflection (with runtime code compilation) in this way, I think you're better off with a language like Python. But I don't think you really want to use reflection in this way.

I think your best bet is to have a second class rather than a trait contain the routines that perform fitness measurement. For example,

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

You then can

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

and look up the right selector by name.

You then have OneMax (and the others) take the selector as a constructor argument, which you can provide from a string via the userInputMap.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文