2011-01-13 2 views
2

Ich habe einen Optimierungsalgorithmus, der die beste Partition eines Graphen findet.Besseres Design für die Verwendung von Funktionszeigern mit variierenden Argumenten

Es gibt viele Maßnahmen für die Qualität einer Partition (die Variable wird optimiert), also dachte ich, es wäre eine gute Idee, Funktionszeiger zu diesen Qualitätsfunktionen zu verwenden und diese in meine Optimierungsalgorithmusfunktion zu übergeben.

Das funktioniert gut, aber das Problem ist, verschiedene Qualitätsfunktionen nehmen einige verschiedene Argumente.

Zum Beispiel eine Qualitätsfunktion ist find_linearised_stability und es erfordert eine markov_time Parameter:

float find_linearised_stability(cliques::Graph<T> &my_graph, cliques::Partition &my_partition, 
           std::vector<float> &markov_times, std::vector<float> &stabilities) 

und wird in der Optimierungsfunktion verwendet:

cliques::find_optimal_partition_louvain(my_new_graph, markov_times, &cliques::find_linearised_stability); 

jedoch eine andere Qualität Funktion find_modularity erfordert keine markov_time Parameter. Natürlich könnte ich es einfach als Argument verwenden und es nicht in der Funktion verwenden, aber das scheint eine schlechte Übung zu sein, und würde unhandlich werden, sobald ich anfange, viele verschiedene Qualitätsfunktionen hinzuzufügen.

Was ist ein besseres Design für diese Art von Situation?

Antwort

6

Verwenden Sie Funktionsobjekte. Einer dieser Funktion können Objekte ein markov_time Mitglied haben, die in der an den Konstruktor übergeben wird:

struct find_linearised_stability { 
    std::vector<float> & markov_times_; 

    find_linearised_stability(std::vector<float> & markov_times) 
     :markov_times_(markov_times) 
    {} 

    float operator() (cliques::Graph<T> &my_graph, cliques::Partition &my_partition, 
       std::vector<float> &stabilities) 
    { 
     // use markov_times_ in here, we didn't need to pass it since it's a member 
    } 
}; 

(Sie Anpassungen Konstantheit/referenceness machen müssen Ihre Bedürfnisse anpassen)

Dann können Sie rufen Sie Ihren Funktion wie folgt aus:

cliques::find_optimal_partition_louvain(my_new_graph, cliques::find_linearised_stability(markov_times)); 

„? welche Art für die Funktion Objekt verwende ich, wenn die ... Funktion deklarieren“

es eine Funktionsschablone machen, dass die Funktion Objekttyp als Templat Parameter nimmt, thusly:

template<typename PR> 
whatever find_optimal_partition_louvain(my_new_graph, PR & pr) 
{ 
    ... 
    pr(my_new_graph, partition, stabilities); 
    ... 
} 
+0

Ok, aber dann, welche Art für die Funktion Objekt kann ich verwenden, wenn die Cliquen erklärt :: find_optimal_partition_louvain Funktion? – zenna

+0

@zenna - Machen Sie es zu einer Funktionsvorlage, siehe zum Beispiel aktualisierte Antwort. –

+0

@zenna - Beachten Sie, dass Sie dadurch auch reguläre Funktionen weitergeben können, wenn Sie herumliegen, wenn Sie nicht in Funktionsobjektklassen umgewandelt werden möchten. –

2

Ihre einzige Option ist boost :: bind oder etwas ähnliches in einer boost :: -Funktion oder so ähnlich gespeichert.

Wenn das Profiling zeigt, dass zu langsam zu sein, dann werden Sie mit der "schlechten Praxis" Version stecken bleiben, weil jede Alternative mit UB in Konflikt geraten und/oder am Ende so langsam wie die vernünftigere sein wird Alternative.

0
  1. Parameter vorher nicht bekannt ist: in Argument für jede Funktion (Referenz/pointer) das enthält Alle Informationen, jede Funktion verwendet, was immer benötigt wird
  2. Parameter ist bekannt: boost :: bind, z:

Beispielquellcode:

#include <iostream> 
#include <cstddef> 
#include <algorithm> 
#include <boost/bind.hpp> 
using namespace std; 

void output(int a, int b) 
{ 
    cout << a << ", " << b << '\n'; 
} 

int main() 
{ 
    int arr[] = { 1, 2, 3, 4, 5 }; 
    for_each(arr, arr + 5, bind(output, 5, _1)); 
    return 0; 
} 

Ausgänge:

5, 1 
5, 2 
5, 3 
5, 4 
5, 5