2010-02-01 9 views
2

Je travaille sur un framework pour un projet EA (evolutional alg) dans Scala. En cela, j'ai un trait qui implémente le code EA commun et laisse le code spécifique au problème comme la conversion du génotype et les tests de fitness aux classes qui implémentent ce trait. Cependant, je ne veux pas implémenter complètement le trait avant qu'il ne soit réellement exécuté à cause de tester différents protocoles/stratégies de sélection de population. Cela donne le codeImplémenter/instancier une classe abstraite par réflexion dans Scala

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

Lors de l'exécution du protocole/stratégie CHOISI:

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

Les objets SelectionStrategies/SelectionProtocols contient clusures avec des références à d'autres codes dans EAProblem. Ce que je veux maintenant, c'est d'instancier d'autres classes abstraites comme OneMax (j'en ai beaucoup) en utilisant la réflexion (ou un autre mécanisme). 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) 
    */ 
} 

Répondre

1

Je ne sais pas si vous pouvez instancier par la réflexion tout, en même temps, définir les méthodes abstraites. Ou, au moins, pas facilement. Pourquoi ne faites-vous pas ces méthodes dans les fonctions? La façon dont j'irais à ce sujet est quelque chose comme ceci:

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 

Bien sûr, OneMax ne serait pas abstraite alors. Ce qui, en fait, est un peu le point. Vous utilisez ensuite la réflexion pour créer une nouvelle instance de OneMax, qui est raisonnablement simple, et utilisez setSelectionStrategy et setNextGeneration pour définir les fonctions.

0

Si vous voulez vraiment utiliser la réflexion (avec compilation de code d'exécution) de cette façon, je pense que vous êtes mieux avec un langage comme Python. Mais je ne pense pas que vous voulez vraiment utiliser la réflexion de cette façon.

Je pense que votre meilleur pari est d'avoir une deuxième classe plutôt qu'un trait contiennent les routines qui effectuent la mesure de la condition physique. Par exemple,

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

Vous pouvez alors

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

et regarder le sélecteur droit par son nom.

Vous avez alors OneMax (et les autres) prennent le sélecteur comme argument constructeur, que vous pouvez fournir à partir d'une chaîne via l'userInputMap.

1
  1. vous ne pouvez pas instancier classe abstraite
  2. vous n'avez pas besoin classe abstraite

fitness, selectionStrategy, NextGeneration - sont toutes les variables "indépendantes". Ainsi, les attacher dans une interface va à l'encontre de la nature du problème. Essayez ceci:

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: vous pouvez instancier classe abstraite ... avec Cglib ou tel

Questions connexes