Menu
Coddy logo textTech

Funktory i wyrażenia lambda

Część sekcji Programowanie obiektowe ścieżki C++ w Coddy. Lekcja 74 z 104.

Wiele algorytmów STL przyjmuje obiekt wywoływalny, który dostosowuje ich działanie. Widziałeś już lambdy używane z std::transform. Przyjrzyjmy się zarówno funktorom, jak i wyrażeniom lambda — dwóm sposobom tworzenia obiektów wywoływalnych w C++.

Funktor (obiekt funkcyjny) to klasa, która przeciąża operator(), umożliwiając wywoływanie jej instancji jak funkcji:

#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));
    // result: {3, 6, 9, 12}
}

Każda instancja funkcyjna może przechowywać własny, niezależny stan za pomocą zmiennych składowych ustawianych podczas tworzenia — czego nie może zrobić zwykłe wywołanie funkcji. Jednak definiowanie klasy dla prostych operacji jest rozwlekłe. Wyrażenia lambda stanowią zwięzłą alternatywę:

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

Składnia lambdy to [capture](parameters) { body }. Klauzula przechwytywania określa, do których zmiennych zewnętrznych lambda ma dostęp. Użyj [=], aby przechwycić wszystkie przez wartość, [&], aby przechwycić wszystkie przez referencję, lub wymień konkretne zmienne, takie jak [factor] lub [&factor].

Lambdy są szczególnie przydatne do jednorazowych operacji z algorytmami, takimi jak sortowanie według niestandardowych kryteriów:

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); });
// Posortowane według wartości bezwzględnej: {-1, -2, 5, 8}
challenge icon

Wyzwanie

Łatwy

Zbudujmy kalkulator cen, który pokazuje zarówno funktory, jak i wyrażenia lambda służące do stosowania różnych strategii rabatowych do cen produktów.

Rozmieścisz swój kod w dwóch plikach:

  • Discounts.h: Zdefiniuj tutaj funktory rabatowe i funkcje pomocnicze.

    Utwórz funktor o nazwie PercentageDiscount, który przechowuje procent rabatu (jako liczbę całkowitą). Jego operator() powinien przyjmować cenę typu double i zwracać cenę po rabacie. Na przykład rabat 20% od 100 USD powinien zwrócić 80 USD.

    Utwórz kolejny funktor o nazwie FixedDiscount, który przechowuje stałą kwotę do odjęcia (jako wartość typu double). Jego operator() powinien przyjmować cenę i zwracać cenę pomniejszoną o stałą kwotę (ale nigdy mniejszą niż 0).

    Utwórz funkcję o nazwie printPrices, która przyjmuje const std::vector<double>& i wypisuje wszystkie ceny oddzielone spacjami, a na końcu znak nowej linii. Formatuj każdą cenę do dwóch miejsc po przecinku.

  • main.cpp: Wczytaj pięć danych wejściowych (każdą w osobnym wierszu):
    1. Cena pierwszego produktu (double)
    2. Cena drugiego produktu (double)
    3. Cena trzeciego produktu (double)
    4. Procent rabatu do zastosowania (liczba całkowita, np. 20 dla 20%)
    5. Kwota stałego rabatu (double)

    Utwórz tablicę z trzema cenami i pokaż oba podejścia:

    1. Wypisz Original prices:, a następnie ceny
    2. Użyj std::transform z funktorem PercentageDiscount, aby utworzyć nową tablicę cen po rabacie. Wypisz After percentage discount:, a następnie wyniki
    3. Użyj std::transform z funktorem FixedDiscount dla cen początkowych, aby utworzyć kolejną tablicę. Wypisz After fixed discount:, a następnie wyniki
    4. Użyj std::transform z wyrażeniem lambda, które podwaja każdą cenę początkową. Wypisz Premium prices (doubled):, a następnie wyniki
    5. Użyj std::sort z lambdą, aby posortować ceny początkowe w kolejności malejącej. Wypisz Sorted (high to low):, a następnie posortowane ceny

Na przykład dla danych wejściowych 100.00, 50.00, 75.00, 20 i 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 

To wyzwanie pozwala porównać funktory (które przechowują stan, taki jak kwota rabatu) z lambdami (które przechwytują zmienne na potrzeby szybkich operacji wykonywanych bezpośrednio w kodzie). Oba podejścia płynnie współpracują z algorytmami STL, takimi jak std::transform i std::sort.

Spróbuj swoich sił

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

int main() {
    // Wczytaj dane wejściowe
    double price1, price2, price3;
    int percentageDiscount;
    double fixedDiscount;
    
    std::cin >> price1;
    std::cin >> price2;
    std::cin >> price3;
    std::cin >> percentageDiscount;
    std::cin >> fixedDiscount;
    
    // Utwórz wektor z trzema cenami
    std::vector<double> prices = {price1, price2, price3};
    
    // TODO: Wypisz "Original prices:", a następnie ceny za pomocą printPrices
    
    // TODO: Użyj std::transform z funktorem PercentageDiscount
    // Utwórz nowy wektor na wyniki
    // Wypisz "After percentage discount:", a następnie wyniki
    
    // TODO: Użyj std::transform z funktorem FixedDiscount na oryginalnych cenach
    // Utwórz nowy wektor na wyniki
    // Wypisz "After fixed discount:", a następnie wyniki
    
    // TODO: Użyj std::transform z lambdą, która podwaja każdą oryginalną cenę
    // Utwórz nowy wektor na wyniki
    // Wypisz "Premium prices (doubled):", a następnie wyniki
    
    // TODO: Użyj std::sort z lambdą, aby posortować oryginalne ceny malejąco
    // Wypisz "Sorted (high to low):", a następnie posortowane ceny
    
    return 0;
}
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator C++ online