Menu
Coddy logo textTech

Funktoren & Lambda-Ausdrücke

Teil des Abschnitts Objektorientierte Programmierung der C++-Journey von Coddy — Lektion 74 von 104.

Viele STL-Algorithmen akzeptieren ein aufrufbares Objekt, das ihr Verhalten anpasst. Sie haben bereits gesehen, wie Lambdas mit std::transform verwendet werden. Lassen Sie uns sowohl Funktoren als auch Lambda-Ausdrücke untersuchen – zwei Möglichkeiten, aufrufbare Objekte in C++ zu erstellen.

Ein Funktor (Funktionsobjekt) ist eine Klasse, die den operator() überlädt, wodurch Instanzen wie Funktionen aufgerufen werden können:

#include <algorithm>
#include <vector>
#include <iostream>

struct MultiplyBy {
    int factor;
    MultiplyBy(int f) : factor(f) {}
    
    int operator()(int x) const {
        return x * factor;
    }
};

int main() {
    std::vector<int> nums = {1, 2, 3, 4};
    std::vector<int> result(nums.size());
    
    std::transform(nums.begin(), nums.end(), result.begin(), MultiplyBy(3));
    // Ergebnis: {3, 6, 9, 12}
}

Funktoren können den Zustand zwischen Aufrufen beibehalten, was reguläre Funktionen nicht können. Das Definieren einer Klasse für einfache Operationen ist jedoch umständlich. Lambda-Ausdrücke bieten eine prägnante Alternative:

int factor = 3;
std::transform(nums.begin(), nums.end(), result.begin(),
               [factor](int x) { return x * factor; });

Die Lambda-Syntax lautet [capture](parameters) { body }. Die Capture-Klausel gibt an, auf welche äußeren Variablen das Lambda zugreifen kann. Verwenden Sie [=], um alle nach Wert zu erfassen, [&], um alle per Referenz zu erfassen, oder listen Sie spezifische Variablen auf wie [factor] oder [&factor].

Lambdas sind besonders nützlich für einmalige Operationen mit Algorithmen wie dem Sortieren nach benutzerdefinierten Kriterien:

std::vector<int> nums = {5, -2, 8, -1};
std::sort(nums.begin(), nums.end(), 
          [](int a, int b) { return std::abs(a) < std::abs(b); });
// Nach Absolutwert sortiert: {-1, -2, 5, 8}
challenge icon

Aufgabe

Einfach

Lassen Sie uns einen Preisrechner erstellen, der sowohl Funktoren als auch Lambda-Ausdrücke demonstriert, um verschiedene Rabattstrategien auf Produktpreise anzuwenden.

Sie werden Ihren Code in zwei Dateien organisieren:

  • Discounts.h: Definieren Sie hier Ihre Rabatt-Funktoren und Hilfsfunktionen.

    Erstellen Sie einen Funktor namens PercentageDiscount, der einen Rabattprozentsatz (als Ganzzahl) speichert. Sein operator() sollte einen double Preis entgegennehmen und den rabattierten Preis zurückgeben. Zum Beispiel sollte ein Rabatt von 20% auf 100 $ den Wert 80 $ zurückgeben.

    Erstellen Sie einen weiteren Funktor namens FixedDiscount, der einen festen Betrag zum Abziehen (als double) speichert. Sein operator() sollte einen Preis entgegennehmen und den Preis abzüglich des festen Betrags zurückgeben (aber niemals weniger als 0).

    Erstellen Sie eine Funktion namens printPrices, die einen const std::vector<double>& entgegennimmt und alle Preise durch Leerzeichen getrennt ausgibt, gefolgt von einem Zeilenumbruch. Formatieren Sie jeden Preis mit zwei Dezimalstellen.

  • main.cpp: Lesen Sie fünf Eingaben ein (jede in einer separaten Zeile):
    1. Erster Produktpreis (double)
    2. Zweiter Produktpreis (double)
    3. Dritter Produktpreis (double)
    4. Anzuwendender prozentualer Rabatt (Ganzzahl, z. B. 20 für 20%)
    5. Fester Rabattbetrag (double)

    Erstellen Sie einen Vektor mit den drei Preisen und demonstrieren Sie beide Ansätze:

    1. Geben Sie Original prices: gefolgt von den Preisen aus
    2. Verwenden Sie std::transform mit Ihrem PercentageDiscount-Funktor, um einen neuen Vektor mit rabattierten Preisen zu erstellen. Geben Sie After percentage discount: gefolgt von den Ergebnissen aus
    3. Verwenden Sie std::transform mit Ihrem FixedDiscount-Funktor auf den Originalpreisen, um einen weiteren Vektor zu erstellen. Geben Sie After fixed discount: gefolgt von den Ergebnissen aus
    4. Verwenden Sie std::transform mit einem Lambda-Ausdruck, der jeden Originalpreis verdoppelt. Geben Sie Premium prices (doubled): gefolgt von den Ergebnissen aus
    5. Verwenden Sie std::sort mit einem Lambda, um die Originalpreise in absteigender Reihenfolge zu sortieren. Geben Sie Sorted (high to low): gefolgt von den sortierten Preisen aus

Zum Beispiel mit den Eingaben 100.00, 50.00, 75.00, 20 und 15.00:

Original prices: 100.00 50.00 75.00 
After percentage discount: 80.00 40.00 60.00 
After fixed discount: 85.00 35.00 60.00 
Premium prices (doubled): 200.00 100.00 150.00 
Sorted (high to low): 100.00 75.00 50.00 

Diese Herausforderung ermöglicht es Ihnen, Funktoren (die einen Zustand wie den Rabattbetrag beibehalten) mit Lambdas (die Variablen für schnelle Inline-Operationen erfassen) zu vergleichen. Beide Ansätze arbeiten nahtlos mit STL-Algorithmen wie std::transform und std::sort zusammen.

Spickzettel

STL-Algorithmen akzeptieren aufrufbare Objekte, um ihr Verhalten anzupassen. Zwei gängige Ansätze sind Funktoren und Lambda-Ausdrücke.

Ein Funktor ist eine Klasse, die den operator() überlädt, wodurch Instanzen wie Funktionen aufgerufen werden können:

struct MultiplyBy {
    int factor;
    MultiplyBy(int f) : factor(f) {}
    
    int operator()(int x) const {
        return x * factor;
    }
};

std::transform(nums.begin(), nums.end(), result.begin(), MultiplyBy(3));

Funktoren können einen Zustand zwischen Aufrufen beibehalten, erfordern jedoch ausführliche Klassendefinitionen.

Lambda-Ausdrücke bieten eine prägnante Alternative mit der Syntax [capture](parameters) { body }:

int factor = 3;
std::transform(nums.begin(), nums.end(), result.begin(),
               [factor](int x) { return x * factor; });

Die Capture-Klausel gibt an, auf welche externen Variablen das Lambda zugreifen kann:

  • [=] - alle per Wert erfassen (capture by value)
  • [&] - alle per Referenz erfassen (capture by reference)
  • [factor] - spezifische Variable per Wert erfassen
  • [&factor] - spezifische Variable per Referenz erfassen

Lambdas eignen sich gut für Algorithmen wie std::sort für benutzerdefinierte Sortierkriterien:

std::sort(nums.begin(), nums.end(), 
          [](int a, int b) { return std::abs(a) < std::abs(b); });

Probier es selbst

#include <iostream>
#include <vector>
#include <algorithm>
#include "Discounts.h"

int main() {
    // Eingaben lesen
    double price1, price2, price3;
    int percentageDiscount;
    double fixedDiscount;
    
    std::cin >> price1;
    std::cin >> price2;
    std::cin >> price3;
    std::cin >> percentageDiscount;
    std::cin >> fixedDiscount;
    
    // Einen Vektor mit den drei Preisen erstellen
    std::vector<double> prices = {price1, price2, price3};
    
    // TODO: "Original prices:" gefolgt von den Preisen mittels printPrices ausgeben
    
    // TODO: std::transform mit dem PercentageDiscount Funktor verwenden
    // Einen neuen Vektor für die Ergebnisse erstellen
    // "After percentage discount:" gefolgt von den Ergebnissen ausgeben
    
    // TODO: std::transform mit dem FixedDiscount Funktor auf die Originalpreise anwenden
    // Einen neuen Vektor für die Ergebnisse erstellen
    // "After fixed discount:" gefolgt von den Ergebnissen ausgeben
    
    // TODO: std::transform mit einem Lambda verwenden, das jeden Originalpreis verdoppelt
    // Einen neuen Vektor für die Ergebnisse erstellen
    // "Premium prices (doubled):" gefolgt von den Ergebnissen ausgeben
    
    // TODO: std::sort mit einem Lambda verwenden, um die Originalpreise in absteigender Reihenfolge zu sortieren
    // "Sorted (high to low):" gefolgt von den sortierten Preisen ausgeben
    
    return 0;
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung