Funktionale Programmierung in C++: von A bis Z
Viele Entwickler, die mit Python oder JavaScript vertraut sind, kennen die leistungsstarken Werkzeuge der funktionalen Programmierung: Lambdas, map, filter, reduce. Aber wussten Sie, dass C++ nicht weniger flexible Möglichkeiten für den funktionalen Stil bietet? In diesem Artikel werden wir die funktionale Programmierung in C++ von den grundlegenden Konzepten bis zu fortgeschrittenen Techniken durchgehen. Sie erfahren, wie Sie mit den modernen Funktionen von C++11/14/17 prägnanteren, sichereren und ausdrucksstärkeren Code schreiben können.
1. Grundlagen des funktionalen Ansatzes: Lambda-Ausdrücke und std::function
Funktionale Programmierung in C++ beginnt mit Lambda-Ausdrücken. Sie ermöglichen es, anonyme Funktionen direkt am Verwendungsort zu erstellen. Die Syntax eines Lambdas ist einfach:
// Basis-Lambdaauto sum = [](int a, int b) -> int { return a + b; };std::cout << sum(5, 3); // 8
// Lambda mit Variablen-Captureint multiplier = 3;auto multiply = [multiplier](int x) { return x * multiplier; };std::cout << multiply(7); // 21Das entscheidende Merkmal von Lambdas ist der Kontext-Capture. Sie können Variablen per Wert ([=]), per Referenz ([&]) oder kombiniert erfassen. Dies ist die Grundlage von Closures, die das Fundament des funktionalen Stils bilden.
Zum Speichern und Übergeben von Lambdas wird std::function verwendet – ein polymorpher Wrapper für aufrufbare Objekte:
#include <functional>
std::function<int(int, int)> operation;
operation = [](int a, int b) { return a + b; };std::cout << operation(10, 20); // 30
operation = [](int a, int b) { return a * b; };std::cout << operation(10, 20); // 200Tipp: Verwenden Sie auto für Lambdas, wenn Sie sie nicht als Typ speichern müssen. std::function fügt einen geringen Overhead hinzu, daher sollte es nur bei Bedarf an Polymorphie eingesetzt werden.
2. STL-Algorithmen: map, filter, reduce selbst gemacht
In C++ gibt es keine eingebauten Funktionen map, filter, reduce wie in Python, aber die STL bietet leistungsstarke Algorithmen, die diese ersetzen. Betrachten wir die wichtigsten:
2.1. Map – Transformation von Elementen
Statt map verwenden Sie std::transform:
#include <algorithm>#include <vector>
std::vector<int> numbers = {1, 2, 3, 4, 5};std::vector<int> squares(numbers.size());
std::transform(numbers.begin(), numbers.end(), squares.begin(), [](int n) { return n * n; });// squares: {1, 4, 9, 16, 25}2.2. Filter – Filtern nach Bedingung
Das Äquivalent zu filter ist std::copy_if oder std::remove_if (mit anschließendem Löschen):
std::vector<int> numbers = {1, 2, 3, 4, 5, 6};std::vector<int> evens;
std::copy_if(numbers.begin(), numbers.end(), std::back_inserter(evens), [](int n) { return n % 2 == 0; });// evens: {2, 4, 6}2.3. Reduce – Faltung einer Sammlung
Für reduce verwenden Sie std::accumulate aus <numeric>:
#include <numeric>
std::vector<int> numbers = {1, 2, 3, 4, 5};int sum = std::accumulate(numbers.begin(), numbers.end(), 0, [](int acc, int n) { return acc + n; });// sum = 15Fortgeschrittenes Beispiel: Kombinieren von map, filter und reduce zur Datenverarbeitung:
std::vector<std::string> names = {"Alice", "Bob", "Charlie", "David"};// Summe der Längen von Namen, die mit 'A' oder 'C' beginnenint result = std::accumulate(names.begin(), names.end(), 0, [](int acc, const std::string& name) { if (name[0] == 'A' || name[0] == 'C') { return acc + name.length(); } return acc;