Menu
Coddy logo textTech

Функторы и лямбда-выражения

Часть раздела Объектно-ориентированное программирование путешествия по C++ на Coddy. Урок 74 из 104.

Многие алгоритмы STL принимают вызываемый объект, который настраивает их поведение. Вы уже видели лямбда-выражения, используемые с std::transform. Давайте рассмотрим functors и lambda expressions — два способа создания вызываемых объектов в C++.

functor (объект функции) — это класс, который перегружает operator(), позволяя вызывать его экземпляры как функции:

#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}
}

Каждый экземпляр functor может хранить собственное независимое состояние через переменные-члены, заданные при создании, — то, чего не может сделать обычный вызов function. Однако определение class для простых операций многословно. Выражения lambda предоставляют лаконичную альтернативу:

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

Синтаксис lambda имеет вид [capture](parameters) { body }. Предложение захвата указывает, к каким внешним переменным lambda может обращаться. Используйте [=], чтобы захватить все по значению, [&], чтобы захватить все по ссылке, или перечислите конкретные переменные, например [factor] или [&factor].

Лямбда-выражения особенно полезны для разовых операций с алгоритмами, например для сортировки по пользовательским критериям:

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); });
// Отсортировано по абсолютному значению: {-1, -2, 5, 8}
challenge icon

Задание

Легко

Давайте создадим калькулятор цен, демонстрирующий использование функторов и лямбда-выражений для применения различных стратегий discount к ценам товаров.

Вы организуете код в двух файлах:

  • Discounts.h: определите здесь функторы discount и вспомогательные функции.

    Создайте функтор с именем PercentageDiscount, который хранит процент discount (как целое число). Его operator() должен принимать цену типа double и возвращать цену с учетом discount. Например, discount в размере 20% от $100 должен возвращать $80.

    Создайте другой функтор с именем FixedDiscount, который хранит фиксированную сумму для вычитания (как double). Его operator() должен принимать цену и возвращать цену за вычетом фиксированной суммы (но never меньше 0).

    Создайте function с именем printPrices, которая принимает const std::vector<double>& и выводит все цены, разделенные пробелами, с последующим newline. Отформатируйте каждую цену с двумя знаками после десятичной точки.

  • main.cpp: считайте пять inputs (каждый в отдельной строке):
    1. Цена первого товара (double)
    2. Цена второго товара (double)
    3. Цена третьего товара (double)
    4. Применяемый процент discount (integer, например, 20 для 20%)
    5. Фиксированная сумма discount (double)

    Создайте вектор с тремя ценами и продемонстрируйте оба подхода:

    1. Выведите Original prices:, followed by ценами
    2. Используйте std::transform с вашим функтором PercentageDiscount, чтобы создать новый вектор цен с учетом discount. Выведите After percentage discount:, followed by результатами
    3. Используйте std::transform с вашим функтором FixedDiscount для исходных цен, чтобы создать еще один вектор. Выведите After fixed discount:, followed by результатами
    4. Используйте std::transform с лямбда-выражением, которое удваивает each исходную цену. Выведите Premium prices (doubled):, followed by результатами
    5. Используйте std::sort с лямбда-выражением, чтобы отсортировать исходные цены в descending order. Выведите Sorted (high to low):, followed by отсортированными ценами

Например, для inputs 100.00, 50.00, 75.00, 20 и 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 

Это задание позволяет сравнить функторы (которые сохраняют состояние, например сумму discount) с лямбда-выражениями (которые захватывают переменные для быстрых встроенных операций). Оба подхода seamlessly работают с алгоритмами STL, такими как std::transform и std::sort.

Попробуйте сами

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

int main() {
    // Считать входные данные
    double price1, price2, price3;
    int percentageDiscount;
    double fixedDiscount;
    
    std::cin >> price1;
    std::cin >> price2;
    std::cin >> price3;
    std::cin >> percentageDiscount;
    std::cin >> fixedDiscount;
    
    // Создать вектор с тремя ценами
    std::vector<double> prices = {price1, price2, price3};
    
    // TODO: Print "Original prices:" followed by the prices using printPrices
    
    // TODO: Use std::transform with PercentageDiscount functor
    // Создать новый вектор для результатов
    // Print "After percentage discount:" followed by the results
    
    // TODO: Use std::transform with FixedDiscount functor on original prices
    // Создать новый вектор для результатов
    // Print "After fixed discount:" followed by the results
    
    // TODO: Use std::transform with a lambda that doubles each original price
    // Создать новый вектор для результатов
    // Print "Premium prices (doubled):" followed by the results
    
    // TODO: Use std::sort with a lambda to sort original prices in descending order
    // Print "Sorted (high to low):" followed by the sorted prices
    
    return 0;
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор C++