Funktionsobjekte
Content |
Funktionsobjekte sind alle Objekte, die wie eine Funktion benutzt werden können. Dies schließt
-
reguläre Funktionen,
-
Template-Funktionen und
-
Objekte ein, deren Klasse einen Funktionsoperator aufweist.
Mit Funktionsobjekten werden zahlreiche Algorithmen parametrisiert. Im folgenden Beispiel wird std::copy_if aus #include <algorithm> genutzt, um mit Hilfe eines Prädikats bei einer mit Iteratoren spezifizierten Menge von Objekten eine Auswahl zu treffen:
#include <algorithm> #include <iomanip> #include <iostream> #include <numeric> #include <random> #include <vector> bool is_even(const int& value) { return value % 2 == 0; } int main() { /* initialize values and fill it with shuffled values 1..100 */ std::vector<int> values(100); std::iota(values.begin(), values.end(), 1); std::shuffle(values.begin(), values.end(), std::mt19937(2)); /* select all even values out of it */ std::vector<int> selected_values; std::copy_if(values.begin(), values.end(), std::back_inserter(selected_values), is_even); /* print selected values */ int count = 0; for (auto value: selected_values) { std::cout << std::setw(4) << value; if (++count % 10 == 0) std::cout << std::endl; } }
Ein paar Erläuterungen zu dem Beispiel:
-
Mit std::iota kann eine mit Iteratoren spezifizierte Menge von Objekten mit sukzessiven numerischen Werten gefüllt werden. Der dritte Parameter spezifiziert den ersten Wert, hier eine 2. Der Name iota geht zurück auf einen entsprechend benannten Operator der Programmiersprache APL. Siehe https://stackoverflow.com/questions/9244879/what-does-iota-of-stdiota-stand-for
Für std::iota wird #include <numeric> benötigt.
-
Mit std::shuffle kann eine durch Iteratoren spezifizierte Objektmenge pseudo-zufällig gemischt werden. Als Seed-Wert wird hier 1 verwendet, um die Ausgabe reproduzierbar zu halten.
-
Dann folgt die Auswahl m Hilfe von std::copy_if, wobei der vierte Parameter das Prädikat angibt.
Ein Prädikat erhält ein Objekt (nicht einen Iterator!) und liefert dann einen bool-Wert zurück, um festzustellen, ob für das Objekt das Prädikat erfüllt ist oder nicht.
In diesem Beispiel wird die ganz einfache reguläre Funktion is_even hierfür eingesetzt.
Für std::copy_if wird der Header <algorithm> benötigt.
-
Die Ausgabe erfolgt in gleich weiten Spalten. Dies gelingt mit std::setw, das die vorgesehene Weite für die folgende numerische Ausgabe festlegt. Hierzu wird der Header <iomanip> benötigt.
So sieht die Ausführung aus:
theon$ g++ -Wall -o select-even select-even.cpp theon$ ./select-even 6 20 18 88 48 70 90 54 56 64 42 76 52 96 60 58 34 2 98 66 46 8 10 26 14 86 4 32 92 84 62 50 36 74 16 38 72 30 24 82 68 22 28 78 94 44 100 12 80 40 theon$
Natürlich kann die Funktion auch durch eine Klasse mit einem passenden Funktionsoperator ersetzt werden:
#include <algorithm> #include <iomanip> #include <iostream> #include <numeric> #include <random> #include <vector> struct IsEven { bool operator()(const int& value) { return value % 2 == 0; } }; int main() { /* initialize values and fill it with shuffled values 1..100 */ std::vector<int> values(100); std::iota(values.begin(), values.end(), 1); std::shuffle(values.begin(), values.end(), std::mt19937(2)); /* select all even values out of it */ std::vector<int> selected_values; std::copy_if(values.begin(), values.end(), std::back_inserter(selected_values), IsEven()); /* print selected values */ int count = 0; for (auto value: selected_values) { std::cout << std::setw(4) << value; if (++count % 10 == 0) std::cout << std::endl; } }
theon$ diff -U 2 select-even.cpp select-even2.cpp --- select-even.cpp Thu Jun 21 10:58:03 2018 +++ select-even2.cpp Thu Jun 21 10:57:47 2018 @@ -6,7 +6,9 @@ #include-bool is_even(const int& value) { - return value % 2 == 0; -} +struct IsEven { + bool operator()(const int& value) { + return value % 2 == 0; + } +}; int main() { @@ -19,5 +21,5 @@ std::vector selected_values; std::copy_if(values.begin(), values.end(), - std::back_inserter(selected_values), is_even); + std::back_inserter(selected_values), IsEven()); /* print selected values */ theon$ g++ -Wall -o select-even2 select-even2.cpp theon$ ./select-even2 6 20 18 88 48 70 90 54 56 64 42 76 52 96 60 58 34 2 98 66 46 8 10 26 14 86 4 32 92 84 62 50 36 74 16 38 72 30 24 82 68 22 28 78 94 44 100 12 80 40 theon$
Das bringt hier zunächst nur etwas mehr Text. Aber auf Klassen basierende Funktionsobjekte haben einen Vorteil: Sie können einen Status verwalten und diesen in die Entscheidung einfließen lassen.
Aufgabe
Ersetzen Sie die Prädikatsklasse IsEven durch ein Prädikat Increasing, das das erste Element immer akzeptiert und die weiteren nur dann, wenn sie monoton steigend sind.
Vorlage
#include <algorithm> #include <iomanip> #include <iostream> #include <numeric> #include <random> #include <vector> struct IsEven { bool operator()(const int& value) { return value % 2 == 0; } }; int main() { /* initialize values and fill it with shuffled values 1..100 */ std::vector<int> values(100); std::iota(values.begin(), values.end(), 1); std::shuffle(values.begin(), values.end(), std::mt19937(2)); /* select all even values out of it */ std::vector<int> selected_values; std::copy_if(values.begin(), values.end(), std::back_inserter(selected_values), IsEven()); /* print selected values */ int count = 0; for (auto value: selected_values) { std::cout << std::setw(4) << value; if (++count % 10 == 0) std::cout << std::endl; } }