Compare-Funktion für std::sort in abstraker Klasse mit Template



  • Hi!

    Ich arbeite momentan an einer kleiner Lib für Evolutionäre Algorithmen. Ziel ist es möglichst einfach einen EA für ein bestimmtes Problem zu implementieren. Hierzu habe ich ein, meiner Meinung nach, relativ komplexes Klassendesign:

    template <typename T> class Population abstract {
    	private:
    		std::vector<std::vector<T>> genoms; // first vector contains size vectors. Each of these vector has len=genomLen. Each vector represents a indidual
    
    		std::vector<T> recombine(std::vector<T> i1, std::vector<T> i2);
    		std::vector<T> mutate(std::vector<T> genom); //mutation function
    		bool operator () (std::vector<T> individual1, std::vector<T> individual2) const;
                    //...
    	public:
                    //...
    		virtual std::string individualToString(T individual) = 0; // gives a string represantation of T
    		virtual std::vector<T> getIndividual() = 0; //get some individual
    		virtual double fitness(std::vector<T> genom) = 0; //the fitness function
    };
    

    Bei einem EA hat man ein Individum/Chromosom, welches aus einzelnen Teilen besteht. Diese einzelnen Teile sind vom Typ T. Die Klasse "Population" muss überschrieben werden, damit die Fitness-Funktion vorliegt. "getIndividual()" ist eine Funktion die ein zufälliges Individuum zurückgeben soll - das brauche ich um eine zufällige Startverteilung zu "bauen". "individualToString" soll T als String zurückgeben. Normalerweise würde man eher vorschreiben, dass T eine Klasse mit einer Art "toString()" Funktion sein sollte. Das werde ich evt. später ergänzen, nur momentan hatte ich für Tests keine Lust für einen einfachen integer direkt eine neue Klasse anzulegen.

    Das Ganze funktioniert jetzt auch, nur bin ich mit dem Design etwas unzufrieden. Zumindest scheint mir das Template plus die abstrakte Klasse irgendwie "doppelt gemoppelt". Ich weiß momentan nicht,wie ich das anders implementieren könnte, aber irgendwie sieht das unschön aus. Gerade die Funktion "getIndividual()" z.B. sieht ein wenig unnötig aus. Aber ich wusste sonst nicht, wie ich bei beliebigen Objekten an eine zufällige Startverteilung kommen sollte.

    Jetzt unabhängig vom Design möchte ich genoms nach ihrer Fitnessfunktion sortieren, was mir einige schwierigkeiten macht. Soweit ich gelesen habe, nimmt std::sort ungerne Klassenfunktionen als Sortierfunktionen an, daher gibt es den Workaround mit dem ()-Operator:

    template <typename T> bool Population<T>::operator () (std::vector<T> individual1, std::vector<T> individual2) const {
    	return fitness(individual1) > fitness(individual2);
    }
    
    //...Mit dem Aufruf:
    std :: sort(genoms.begin(), genoms.end(), *this);
    //...
    

    VS 2010 sagt mir dazu: "Failed to specialize function template 'void std::sort(_RanIt,_RanIt,_Pr)".
    Wenn ich das jetzt richtig verstehe, übergebe ich hier den Zeiger (als Call by Value) auf Population. Population muss ja vom User überschrieben werden, sodass dieser die Fitnessfunktion bereitstellt. Da es per Call by Value läuft habe ich also an dieser Stelle auch Zugriff auf die Fitnessfunktion. Was hier nun aber fehlt ist die Spezialisierung auf den Typparameter T, aber wieso? Und wie bekomme ich das hin?

    Wie gesagt, ich bin mit dem allg. Klassenaufbau nicht so 100% zufrieden, aber sehe momentan keine andere Möglichkeit. In der ersten Version hatte ich Funktionspointer benutzt, das lief auch ganz gut. Aber da ich ja C++ nutze wollte ich schon in Richtung OOP gehen und da fande ich abstrakte Klassen eleganter 😉

    Gruß

    Bubbles147

    P.S: Ich habe mich bisher nie sehr intensiv mit Template-Programmierung und abstrakten Klassen in C++ beschäftigt. Ich habe intensiv Java programmiert und bin für einige Projekte auf C++ umgestiegen. Ich bin mir durchaus bewusst, dass gerade in Sachen OOP ein riesen Unterschied zwischen Java und C++ besteht. Daher versuche ich die Probleme so C++-like wie es geht zu lösen. Also wenn hier nun etwas total atypisch für C++ ist oder man in C++ gewissen Dinge einfach anders macht (weil es einfacher ist), dann sagts mir bitte 😉



  • Die Population als Comparator z nehmen klingt irgendwie sinnfrei. Versuchs mal mit einem Lambda, da du ja eh MSVC2010 benutzt.



  • Hey!

    Danke für den Tipp! Hätte zwar nie gedacht, dass ich nochmal eine lambda-Abstraktion in meinem Leben benutze, aber hat direkt gefunkt. Danke 🙂



  • Oh, Lambdas sind eines der ganz großen Dinge aus C++11 🙂
    Grade bei solchen Algorithmen sind die Dinger grandios 🙂



  • abstract ist kein Standard-C++.



  • template <typename T> bool Population<T>::operator () (std::vector<T> individual1, std::vector<T> individual2) const {
    return fitness(individual1) > fitness(individual2);
    }

    Absicht dass du hier kopierst und nicht per Referenz übergibst?



  • 314159265358979 schrieb:

    abstract ist kein Standard-C++.

    Hatte mich schon gewundert, warum ich das in einigen Beispielen gefunden hatte und in anderen wiederum nicht. Komisch was MS da wieder für "eigene" Sachen einbaut. Habs mal rausgenommen.

    Ethon schrieb:

    Absicht dass du hier kopierst und nicht per Referenz übergibst?

    Jaein. Zum Einen sitzt mit das explizite Call by Reference noch nicht so in den Fingern und zum Anderen war ich mir nicht sicher, wie ich die Fitness-Funktion genau aufbauen möchte. Aber danke für den Hinweis, ich hab das erstmal bei ein paar anderen Funktionen umgesetzt 😉

    Mal so allgemein gefragt: Sind die Lambda-Abstraktionen derzeit nur auf MSVS beschränkt oder kann ich die auch mit einem gcc/g++ kompilieren ohne mir großartig einen abzubrechen?



  • Erstens heißt es "Lambda Funktionen", zweitens hat der GCC die schon lange vor MSVC gehabt. 😉



  • 314159265358979 schrieb:

    Erstens heißt es "Lambda Funktionen"

    zweitens schreibt es sich Lambda-Funktionen.


Log in to reply