7

Ich habe an einem Projekt mit dem Namen: Funktionale Programmierfunktionen von C++ 11/14 (für eines meiner Fächer an der Universität) gearbeitet. Es gibt mehrere bestehende Quellen und ähnliche Präsentationen über solche Themen und ich fand eine nicht so lange her, die mehrere Code-Schnipsel, die ich nicht vollständig verstanden habe (und irgendwie können sie mit der funktionalen Programmierung verbunden sein) enthalten. Schnipseln A und B gehörte Rekursion und C gehörte lazy evaluation. Ich möchte sie unten mit Ihnen teilen:C++ funktionale Programmierung Code-Schnipsel

Snippet A:

#include <iostream> 

template <int N> 
struct Factorial { 
    static int const val = N * Factorial<N - 1>::val; 
}; 

template <> 
struct Factorial <0> { 
    static int const val = 1; 
}; 

int main() { 
    int factorial_of_6 = Factorial<6>::val; 
    std::cout << factorial_of_6 << std::endl; 
    return 0; 
} 

war der Punkt hier die Zeitauswertung kompilieren (um Laufzeit Berechnungen zu vermeiden und die Leistung zu verbessern)? Oder gibt es noch andere Vorteile?

Snippet B:

#include <iostream> 

template <int ...> 
struct my_sum; 

template <> 
struct my_sum <> { 
    static const int value {0}; 
}; 

template <int i, int ... tail> 
struct my_sum <i, tail ...> { 
    static const int value = i + my_sum<tail ...>::value; 
}; 

int main() { 
    int sum {my_sum<1, 2, 3, 4, 5>::value}; 
    std::cout << sum << std::endl; 
    return 0; 
} 

Gleiche Frage wie oben gilt.

Und hier ist eine andere Schnipsel, die vielleicht ähnlich ist:

Snippet C:

#include <iostream> 

template <typename... Args> 
void some_function (Args ...) { 
    std::cout << sizeof...(Args) << std::endl; 
} 

int main() { 
    some_function ("Every little thing gonna be alright...", 1.0/0.0); 
    return 0; 
} 

"Die Präsentation sagte: C++ ist bestrebt, aber die folgenden funktionieren wird." Ist es der Punkt, dass, bis mir die angegebenen Ausdrücke egal sind, ich die Menge von ihnen erzählen kann?

Bitte seien Sie so spezifisch und detailliert wie möglich, und vielen Dank für Ihre Geduld und Hilfe im Voraus. :)

+0

Für C, Args sind nicht faul ausgewertet. Es wird ausgewertet (dann kann die Optimierung mit * as-if * -Regel sie verwerfen). – Jarod42

Antwort

5

Snippet A

Dies wird Template Metaprogramming genannt, die im Grunde eine Technik Vorlagen bei der Kompilierung zu erzeugen, unter Verwendung von Code. Dies erhöht die Laufzeitleistung, da die Berechnungen nicht zur Laufzeit, sondern zur Kompilierungszeit ausgeführt werden.

Schnipsel A berechnet die faktorielle einer gegebenen Anzahl zur Kompilierzeit:

template <int N> 
struct Factorial { 
    static int const val = N * Factorial<N - 1>::val; 
}; 

Dies definiert eine structFactorial als Vorlage, die ein int nimmt. In dieser struct gibt es eine static const Variable.Die Variable ist static, so dass Sie es nicht eine Instanz von Factorial erstellen müssen zugreifen, können Sie einfach Factorial::val anstelle von

Factorial factorial; 
factorial.val; 

Die Variable ist const weil die Fakultäts einer bestimmten Zahl ist immer die Gleiches, und weil das Projekt nicht kompiliert wird, wenn es nicht const ist, weil der Compiler keine Möglichkeit hat zu wissen, ob Sie die Variable an einem anderen Ort ändern.

Die Variable hat einen Wert von N * Factorial<N - 1::val;, der im Grunde N mit dem Faktor der vorherigen Zahl multipliziert. Dies liegt daran, wie Faktoren definiert sind (3! = 2! * 3 = 1! * 2 * 3 = 1 * 2 * 3 = 6).

template <> 
struct Factorial <0> { 
    static int const val = 1; 
}; 

Dies definiert eine völlig spezialisierte struct für N = 0. Das ist wirklich wichtig, sonst wird die Rekursion, die in der vorherigen Funktion verwendet wird, niemals aufhören.

Dann ist das Erhalten der Fakultät einer Zahl N einfach, Factorial<N>::val. Dies wird zur Kompilierzeit berechnet.


Snippet B

Dies ist auch Template Metaprogramming.

template <int ...> 
struct my_sum; 

Dies definiert eine leere Vorlage struct, die ein int... (ein Parameter Pack) hat, so dass es spezialisiert werden kann (siehe nächsten Punkt).

template <> 
struct my_sum <> { 
    static const int value {0}; 
}; 

Dies ist spezialisiert die structmy_sum, wenn keine Vorlage Argumente gegeben werden (dies ist wegen der Parameter Pack, die leer sein können, und so wird die Vorlage Argumente leer sein, wenn die Parameter Pack erweitert wird). Die value ist static und const wegen der gleichen Gründe wie zuvor, und es ist 0 ein initializer list initialisiert mit (für ein int, gibt es keinen Unterschied zwischen int i = 0; und int i{ 0 };).

template <int i, int ... tail> 
struct my_sum <i, tail ...> { 
    static const int value = i + my_sum<tail ...>::value; 
}; 

Dies definiert die structmy_sum als Vorlage die Vorlage 2 Argumente annimmt, ein int und ein int Parameterpaket. Dies wird verwendet, um den Wert des ersten Werts eines Parameterpakets abzurufen, da Sie ein Parameterpaket nicht indizieren können (es handelt sich nicht um ein Array). Dann wird value initialisiert, wie i (der erste Wert der Packung) plus der value der anderen Werte als Parameterpaket, das erweitert wird (unter Verwendung der ...):

int sum = my_sum<1, 2, 3>::value; 

Diese my_sum<int i, int... tail> nennt, i ist 1 und tail ist 2, 3. value ist i + my_sum<tail...>::value, so ist es 1 + my_sum<2, 3>. my_sum<2, 3> ruft die gleiche Funktion erneut auf, 2 + my_sum<3>::value. Jetzt haben wir 1 + 2 + my_sum<3>::value. my_sum<3>::value ruft wieder die gleiche Funktion auf, aber jetzt ist das Parameterpaket leer! So ist value1 + 2 + 3 + my_sum<>::value.my_sum<>::value ist 0 (wie definiert) und so value = 1 + 2 + 3 + 0.


Snippet C

Der Ausdruck wird ausgewertet, aber das Programm nicht abstürzt, weil der Ausdruck, wenn bewertet, ein double ist. Nur wenn der Ausdruck ein int ist, stürzt es mit einem Integer division by zero exception ab. Wenn Sie dies tun sollten:

int zero = 0; 
double d = 1.0/zero; 

Dann d hätte den Wert inf.

Die Funktion some_function ist eine Template-Funktion, die als Template-Parameter ein Parameter-Pack übernimmt. Dann ruft es sizeof... auf, das die Elemente im Parameterpack zählt und es mit std::cout ausgibt.

+0

Vielen Dank für Ihre ausführliche Antwort! Aber ich würde gerne noch einen fragen, wenn es dir nichts ausmacht: Ich habe deine Erklärung über C, aber macht es überhaupt Sinn, wenn ich ganze Zahlen benutze? Der Ausdruck wird ausgewertet und das Programm stürzt ab. War das überhaupt einer faulen Bewertung ähnlich? (Genau wie Jarod42 oben erwähnt.) – laszlzso

+0

@ ZsoltLászló Ich denke nicht, denn wie Jarod42 gesagt hat, gibt es keine eingebaute faule Auswertung in C++ – Rakete1111

+0

Verstanden, danke. Übrigens habe ich seither den Code ausgeführt: Durch das Einfügen von -O3 in die Compileroptionen konnten Ausnahmen vermieden werden (und die Ausgabe war die erwartete 2). – laszlzso