2 Stimmen

Implementierung/Instantiierung einer abstrakten Klasse über Reflexion in Scala

Ich arbeite an einem Framework für ein EA (Evolutionary Alg) Projekt in Scala. Darin habe ich einen Trait, der allgemeinen EA-Code implementiert und problem-spezifischen Code wie Genotyp-Konvertierung und Fitness-Tests an Klassen überlässt, die diesen Trait implementieren. Allerdings möchte ich den Trait nicht vollständig implementieren, bevor er tatsächlich ausgeführt wird, um verschiedene Protokolle/Strategien für die Populationsauswahl zu testen. Daraus ergibt sich der 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
  }
}

Zur Laufzeit wird das Protokoll/die Strategie gewählt:

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

Die SelectionStrategies/SelectionProtocols-Objekte enthalten Klammern mit Verweisen auf anderen Code in EAProblem.

Was ich jetzt möchte, ist eine Möglichkeit, andere abstrakte Klassen wie OneMax (ich habe viele von ihnen) mit Reflexion (oder einen anderen Mechanismus) zu instanziieren. 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)
    */
}

1voto

Daniel C. Sobral Punkte 290004

Ich weiß nicht, ob man sie durch Reflexion instanziieren kann, während man gleichzeitig die abstrakten Methoden definiert. Oder zumindest nicht einfach.

Warum machen Sie diese Methoden nicht zu Funktionen? Ich würde etwa so vorgehen:

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

Ja, natürlich, OneMax wäre dann nicht abstrakt. Das ist ja auch der Sinn der Sache. Sie verwenden dann Reflection, um eine neue Instanz von OneMax was relativ einfach ist, und verwenden Sie setSelectionStrategy y setNextGeneration um die Funktionen einzustellen.

1voto

Alexey Punkte 8727
  1. Sie können keine abstrakte Klasse instanziieren
  2. Sie brauchen keine abstrakte Klasse

Fitness, selectionStrategy, nextGeneration - sind alle "unabhängige" Variablen. Sie in einer einzigen Schnittstelle zusammenzufassen, widerspricht daher der Natur des Problems. Versuchen Sie dies:

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

Edita: Sie können abstrakte Klasse instanziieren ... mit CGLib oder so

0voto

Rex Kerr Punkte 164629

Wenn Sie Reflection (mit Codekompilierung zur Laufzeit) wirklich auf diese Weise nutzen wollen, sind Sie mit einer Sprache wie Python besser dran. Aber ich glaube nicht, dass Sie Reflection wirklich auf diese Art und Weise verwenden wollen.

Ich denke, dass es am besten ist, wenn die Routinen, die die Fitnessmessung durchführen, in einer zweiten Klasse und nicht in einem Merkmal enthalten sind. Zum Beispiel,

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

Sie können dann

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

und suchen Sie den richtigen Selektor anhand seines Namens.

OneMax (und die anderen) nehmen dann den Selektor als Konstruktorargument, das Sie aus einer Zeichenkette über die userInputMap bereitstellen können.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X