2016-09-14 3 views
4

In funktionsorientierten Sprachen wie Haskell kann man Funktionsdefinitionen um mehrere Achsen der Parametersignatur überladen. C++ unterstützt Anzahl und Typ der Argumente. Andere Sprachen unterstützen Werte Argument und sogar guard Klauseln (Code, den die Argumente für Bedingungen prüft.) Zum Beispiel der Fakultäts Implementierung in Haskell:Gibt es eine Möglichkeit, Funktionssignaturvergleich basierend auf Parameterwerten durchzuführen?

factorial :: (Integral a) => a -> a 
factorial 0 = 1 
factorial n = n * factorial (n - 1) 

Wo die Definition für faktorielle wenn das Argument 0 unterscheidet mich von der Definition für factorial wenn das Argument eine andere Ganzzahl ist.

Ich habe diese Fähigkeit in C++ nicht gefunden und dachte zuerst, dass es schwierig wäre, in der Sprache zu implementieren. Weitere Überlegungen ließen mich denken, dass es eigentlich ziemlich einfach wäre und eine nette Ergänzung zur Sprache wäre, also muss ich es vermissen.

Gibt es eine Möglichkeit, dies entweder in nativer Syntax oder Vorlagen zu tun?

+0

C++ - Vorlagen können auf Integer-Konstanten spezialisiert sein, wodurch ungefähr dasselbe erreicht wird wie in Ihrem Haskell-Beispiel. [Hier ist eine Fakultät Funktion geschrieben mit spezialisierten Vorlagen in C++] (http://stackoverflow.com/q/3082113/464709). –

+0

Ja, dies kann mit der Metaprogrammierung der Vorlage geschehen, aber nur wenn die Parameterwerte zur Kompilierzeit bekannt sind. C++ kann nicht zur Laufzeit auf andere Parameterwerte als den virtuellen Methodenversand zugreifen. – antlersoft

+0

Das sind keine zwei überladenen Funktionen, es ist die Funktion "Fakultät x = Fall x von 0 => 1; n => n * faktoriell (n - 1) 'mit obenaufgestreutem syntaktischem Zucker. – molbdnilo

Antwort

2

ich denke, hier die richtige Antwort geschehen ist, dass es keine exakte entspricht. Noch. Die Template-Spezialisierung ist eng, aber das funktioniert nur zur Kompilierzeit, was die Benutzerfreundlichkeit einschränkt. Wir haben natürlich Verzweigungen, aber das hat eine begrenzte Macht im Vergleich zu dem, was Mustererkennung in anderen funktionalen Programmiersprachen leisten kann.

Es gibt einen Vorschlag zur Zeit für den Mustervergleich in C++: P0095r1 die folgende Definition von factorial erlauben würde, unter der Annahme, Konzepte:

template <Integral I> 
I factorial(I n) { 
    return inspect(n) { 
     0 => 1 
     n => n * factorial(n-1) 
    }; 
} 

ich auf der Syntax nicht ganz sicher bin, aber dann wieder, es nur ein Vorschlag bisher, also könnte sich die Syntax selbst ändern.

2

Es gibt so etwas und es heißt Template-Spezialisierung. Grundsätzlich können Sie neben der allgemeinen Vorlagendefinition eine Vorlage für einen bestimmten Typ definieren. Sie können darüber lesen here

//Main template definition 
template<typename T> 
void foo(T) { std::cout << "Some T\n"; } 

//Specialization for int 
template<> 
void foo(int) { std::cout << "Called with an int!\n"; } 

Die faktorielle Vorlage „Funktion“ auch Template-Spezialisierung verwendet, aber aufgrund der Art der Vorlagen, kann es nur Zeitwerte (Metaprogrammierung) berechnen kompilieren:

template<std::size_t N> 
struct factorial { 
    static constexpr unsigned long long value = N * factorial<N - 1>::value; 
}; 

template<> 
struct factorial<0> { 
    static constexpr unsigned long long value = 1; 
} 

auto foo = factorial<10>::value; 

Soweit ich weiß, gibt es zur Laufzeit (abgesehen von switch/if-Verzweigungen) in einer bestimmten Funktion keine solche Funktion.

0

Kurze Antwort: nein C++ hat keine Haskell-ähnliche Mustererkennung. Erwähnenswert ist auch, dass Sie im Haskell-Beispiel eine einzige Funktion haben, anstatt zwei, aber Sie haben nur eine schönere Syntax zum Überprüfen der Werte von Eingaben. Beim Überladen haben Sie tatsächlich zwei oder mehr Funktionen mit demselben Namen, aber unterschiedlicher Anzahl oder Typen von Argumenten.

Längere/wahrheitsgemäße Antwort: etwas, das dem ähnlich ist, was Sie vorschlagen, ist über template-metaprogramming möglich. Da C++ - Vorlagen Werte als Vorlagenargumente und nicht nur Typen zulassen, können Sie eine solche Funktion tatsächlich erstellen. Die Template-Sprache ist scheinbar Turing-vollständig, so dass man eigentlich alles berechnen kann, was damit berechenbar ist. Natürlich sieht es schrecklich aus und führt zu massiven Kompilierungszeiten und Schwierigkeiten beim Verständnis des Codes nach dem ersten Schreiben.

0

Runtime Verzweigung wird mit if oder den ternären Operator getan <condition> ? <if-true> : <if-false>.

Überladen einer Funktion erfolgt um Kompilierzeit, so bedeutet es, dass wenn Sie eine Überladung einer Funktion basierend auf Werten auswählen möchten, müssen Sie diesen Wert genau bei der Kompilierung kennen.

Hier ist ein Beispiel von der Kompilierung Verzweigung sfinae mit:

template<int n, std::enable_if_t<(n > 1), short> = 0> 
constexpr int factorial(std::integral_constant<int, n>) { 
    return n * factorial(std::integral_constant<n - 1>{}); 
} 

template<int n, std::enable_if_t<(n == 0), short> = 0> 
constexpr int factorial(std::integral_constant<int, n>) { return 1; } 

Hier beachten Sie die in enable_if_t verwendeten Bedingungen. Dadurch wird die Funktion ungültig, wenn die Bedingung nicht erfüllt ist, und der Compiler wird gezwungen, alternative Funktionen auszuprobieren.

Natürlich ist die Syntax nicht so toll. Am besten wäre es eine einzige Implementierung für beide Laufzeit zu haben und Zeit kompilieren, aber für die Sie mit traditioneller Verzweigung muss:

constexpr factorial(int n) { 
    return n == 0 ? 1 : n * factorial(n - 1); 
} 
1

Wenn die Werte zum Zeitpunkt der Kompilierung bekannt sind, kann es mit Vorlagen

erfolgen
//recursively calls itself until N is 1 
template<int N> 
struct factorial<N>{enum{value = N * factorial<N-1>::value};}; 

//at which point, this will be called (stopping the recursion) 
template<> 
struct factorial<1>{enum{value = 1};}; 

Wenn die Werte nur zur Laufzeit bekannt ist, muss die Entscheidung zur Laufzeit

int factorial_recursion(int n){ 
    if(n == 1) 
    return 1; 
    else 
    return n * factorial_recursion(n - 1); 
} 
//or 
int factorial_loop(int n){ 
    int answer = 1; 
    for(int count = n; count > 1; --count) 
    answer *= count; 

    return answer; 
} 
0
int factorial(int n) 
{ 
    switch(n) 
    { 
     case 0: return 1; 
     default: return n * factorial(n - 1); 
    } 
} 
+0

Mein Gedanke war, dass der Compiler dieses "Under-the-Cover" erstellen könnte/könnte. –

+0

@JasonDoege Ich denke, C++ dreht sich alles um Explicitness. Wenn der Compiler hinter dem Rücken der Leute hinter die Kulissen gerät, würde ich wohl gegen das Ethos der Sprache sein. Ich kenne Leute wie Haskell, aber für mich ist die Syntax extrem kryptisch und (anders als in diesem trivialen Fall) extrem schwierig zu verstehen. Ich weiß nicht, vielleicht ist es nur, dass ich ein alter Hacker bin. –

Verwandte Themen