← Назад к вопросам

Что такое функциональное программирование?

1.7 Middle🔥 161 комментариев
#Язык C++

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Что такое функциональное программирование?

Функциональное программирование (Functional Programming, FP) — это парадигма программирования, которая рассматривает вычисления как вычисление значений математических функций и избегает изменяемого состояния (mutable state) и побочных эффектов.

Основные принципы

1. Pure Functions (Чистые функции)

Чистая функция — это функция, которая:

  • Возвращает один и тот же результат для одного и того же входа
  • Не имеет побочных эффектов (не меняет глобальное состояние, не печатает, не пишет в БД)
// ЧИСТАЯ ФУНКЦИЯ
int add(int a, int b) {
    return a + b;  // Всегда возвращает одно и то же для одних входов
}

// НЕ ЧИСТАЯ ФУНКЦИЯ
int globalCounter = 0;

int incrementAndAdd(int a, int b) {
    globalCounter++;  // Побочный эффект!
    return a + b + globalCounter;
}

// НЕ ЧИСТАЯ ФУНКЦИЯ
int getRandomAdd(int a, int b) {
    return a + b + rand();  // Зависит от побочного эффекта rand()
}

2. Immutability (Неизменяемость)

Данные должны быть неизменяемы. Вместо изменения значения создаётся новое:

// IMPERATIVE (изменяемость)
vector<int> nums = {1, 2, 3, 4, 5};
nums[0] = 10;  // Меняем исходный вектор

// FUNCTIONAL (неизменяемость)
vector<int> nums = {1, 2, 3, 4, 5};
vector<int> newNums = nums;
newNums[0] = 10;  // Создаём новый вектор
// nums остаётся неизменным

// Или с помощью higher-order function
vector<int> result = map(nums, [](int x) { return x == 0 ? 10 : x; });

3. First-Class и Higher-Order Functions

Функции — это значения первого класса:

// Higher-order function: принимает функцию как аргумент
template<typename T, typename Func>
vector<T> map(const vector<T>& arr, Func fn) {
    vector<T> result;
    for (const auto& item : arr) {
        result.push_back(fn(item));
    }
    return result;
}

// Использование
vector<int> nums = {1, 2, 3, 4, 5};
vector<int> doubled = map(nums, [](int x) { return x * 2; });
// doubled = {2, 4, 6, 8, 10}

Основные техники функционального программирования

1. Map (Трансформация)

// Применить функцию к каждому элементу
vector<int> nums = {1, 2, 3, 4};
vector<int> squared;

for (int num : nums) {
    squared.push_back(num * num);
}
// squared = {1, 4, 9, 16}

// Или с использованием functional library (C++11+)
transform(nums.begin(), nums.end(), 
          back_inserter(squared),
          [](int x) { return x * x; });

2. Filter (Фильтрация)

// Оставить только элементы, которые удовлетворяют условию
vector<int> nums = {1, 2, 3, 4, 5, 6};
vector<int> evens;

for (int num : nums) {
    if (num % 2 == 0) {
        evens.push_back(num);
    }
}
// evens = {2, 4, 6}

// Или с помощью copy_if
copy_if(nums.begin(), nums.end(),
        back_inserter(evens),
        [](int x) { return x % 2 == 0; });

3. Reduce/Fold (Свёртка)

// Свернуть массив в одно значение
vector<int> nums = {1, 2, 3, 4};
int sum = 0;

for (int num : nums) {
    sum += num;
}
// sum = 10

// Или с помощью accumulate
int sum = accumulate(nums.begin(), nums.end(), 0,
                     [](int acc, int x) { return acc + x; });

// С начальным значением
int product = accumulate(nums.begin(), nums.end(), 1,
                         [](int acc, int x) { return acc * x; });
// product = 24

Практический пример: Data Pipeline

#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
using namespace std;

// Pure functions
auto isEven = [](int x) { return x % 2 == 0; };
auto square = [](int x) { return x * x; };
auto add = [](int acc, int x) { return acc + x; };

int main() {
    vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    
    // Functional pipeline:
    // 1. Фильтруем чётные числа
    vector<int> evens;
    copy_if(numbers.begin(), numbers.end(),
            back_inserter(evens), isEven);
    // evens = {2, 4, 6, 8, 10}
    
    // 2. Возводим в квадрат
    vector<int> squared;
    transform(evens.begin(), evens.end(),
              back_inserter(squared), square);
    // squared = {4, 16, 36, 64, 100}
    
    // 3. Суммируем
    int result = accumulate(squared.begin(), squared.end(), 0, add);
    // result = 220
    
    cout << "Result: " << result << endl;  // 220
    
    return 0;
}

Функции высшего порядка

Compose (Композиция)

// Функция, которая создаёт новую функцию из двух
template<typename A, typename B, typename C>
auto compose(function<C(B)> f, function<B(A)> g) {
    return [f, g](A x) { return f(g(x)); };
}

auto increment = [](int x) { return x + 1; };
auto square = [](int x) { return x * x; };

auto incrementThenSquare = compose(square, increment);
int result = incrementThenSquare(3);  // (3+1)^2 = 16

Partial Application (Частичное применение)

// Превращение функции многих аргументов в функцию одного аргумента
function<int(int)> addFive(int a) {
    return [a](int b) { return a + b; };
}

auto addFiveFunc = addFive(5);
int result = addFiveFunc(3);  // 5 + 3 = 8

Лямбда выражения в C++

// Синтаксис лямбда
auto func = [captures](parameters) -> return_type { body };

// Примеры
auto add = [](int a, int b) { return a + b; };
auto multiply = [](int a, int b) -> int { return a * b; };

// С захватом переменных
int factor = 5;
auto multiplyByFactor = [factor](int x) { return x * factor; };

// Захват по значению [=] или по ссылке [&]
int counter = 0;
auto increment = [&counter]() { counter++; };

Перечисления (List Comprehensions в C++)

// C++ предлагает несколько способов

// 1. Обычный loop
vector<int> nums = {1, 2, 3, 4, 5};
vector<int> result;
for (int n : nums) {
    if (n % 2 == 0) {
        result.push_back(n * n);
    }
}

// 2. STL algorithms
vector<int> evens;
copy_if(nums.begin(), nums.end(),
        back_inserter(evens),
        [](int x) { return x % 2 == 0; });

vector<int> squares;
transform(evens.begin(), evens.end(),
          back_inserter(squares),
          [](int x) { return x * x; });

// 3. Ranges (C++20)
auto squares = nums
    | views::filter([](int x) { return x % 2 == 0; })
    | views::transform([](int x) { return x * x; });

Моноиды и Полугруппы

// Моноид = набор элементов + бинарная операция с ассоциативностью + нейтральный элемент

struct Monoid {
    // Нейтральный элемент (identity)
    static int identity() { return 0; }
    
    // Бинарная операция
    static int combine(int a, int b) { return a + b; }
};

// Использование
vector<int> nums = {1, 2, 3, 4, 5};
int result = accumulate(nums.begin(), nums.end(),
                        Monoid::identity(),
                        [](int a, int b) { return Monoid::combine(a, b); });
// result = 15

Функциональный подход vs Imperative

// IMPERATIVE (традиционный подход)
int total = 0;
for (int i = 0; i < nums.size(); i++) {
    if (nums[i] % 2 == 0) {
        total += nums[i] * nums[i];
    }
}

// FUNCTIONAL (декларативный подход)
int total = accumulate(
    nums | views::filter([](int x) { return x % 2 == 0; })
        | views::transform([](int x) { return x * x; }),
    0,
    plus<int>()
);

Преимущества функционального программирования

  1. Предсказуемость: Pure functions легче понять и тестировать
  2. Параллелизм: Без побочных эффектов легче параллелизировать
  3. Отладка: Легче отследить ошибки
  4. Переиспользуемость: Функции более универсальны
  5. Композиция: Легко комбинировать функции

Недостатки

  1. Производительность: Могут быть временные копии данных
  2. Кривая обучения: Нужно переучиться мыслить иначе
  3. Читаемость: Может быть сложнее читать для новичков
  4. Отсутствие прямого I/O: Побочные эффекты часто необходимы

Вывод

Функциональное программирование — это мощная парадигма, которая при правильном использовании делает код:

  • Надёжнее: Меньше мест для ошибок
  • Понятнее: Логика явна
  • Тестируемее: Pure functions просто тестировать
  • Параллелизируемее: Нет data races

Для backend-разработчиков знание FP-принципов критично при работе с обработкой данных, потоками и системами обработки больших объёмов информации. C++ позволяет использовать FP-подходы, хотя он не является чисто функциональным языком.

Что такое функциональное программирование? | PrepBro