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

Какая основная идея функционального программирования?

1.8 Middle🔥 141 комментариев
#Python Core#Архитектура и паттерны

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

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

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

Какая основная идея функционального программирования

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

Ключевой принцип: Чистые функции

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

  1. Всегда возвращает одинаковый результат для одинаковых входных данных
  2. Не имеет побочных эффектов — не изменяет глобальное состояние
  3. Не зависит от внешнего состояния — использует только переданные аргументы
# НЕЧИСТАЯ функция
global_counter = 0

def impure_add(a: int, b: int) -> int:
    """Зависит от глобального состояния"""
    global global_counter
    global_counter += 1  # Побочный эффект!
    return a + b

# Одинаковый вызов, но счётчик меняется
print(impure_add(5, 3))  # 8, global_counter = 1
print(impure_add(5, 3))  # 8, global_counter = 2

# ЧИСТАЯ функция
def pure_add(a: int, b: int) -> int:
    """Только преобразует входные данные"""
    return a + b

# Одинаковый вызов, одинаковый результат, без эффектов
print(pure_add(5, 3))  # 8
print(pure_add(5, 3))  # 8 (идентично)

Принципы функционального программирования

1. Избегаем изменения состояния (Immutability)

# ПЛОХО — изменяем список
users = ["Alice", "Bob", "Charlie"]
users.append("David")  # Модифицируем оригинальный список
print(users)

# ХОРОШО — создаём новый список
users = ["Alice", "Bob", "Charlie"]
new_users = users + ["David"]  # Оригинальный список не меняется
print(new_users)  # ['Alice', 'Bob', 'Charlie', 'David']
print(users)      # ['Alice', 'Bob', 'Charlie'] — не изменился

# ещё лучше — используем tuple (неизменяемый)
users_tuple = ("Alice", "Bob", "Charlie")
new_users = users_tuple + ("David",)

2. First-class функции (функции как значения)

# Функции можно передавать как аргументы
def apply_operation(a: int, b: int, operation) -> int:
    """Применяет операцию к двум числам"""
    return operation(a, b)

add = lambda x, y: x + y
multiply = lambda x, y: x * y

print(apply_operation(5, 3, add))        # 8
print(apply_operation(5, 3, multiply))   # 15

# Функции возвращают функции
def create_multiplier(factor: int):
    def multiply(x: int) -> int:
        return x * factor
    return multiply

times_3 = create_multiplier(3)
print(times_3(10))  # 30

3. Map, Filter, Reduce — высокоуровневые операции

from functools import reduce
from typing import List, Callable

numbers = [1, 2, 3, 4, 5]

# MAP — преобразует каждый элемент
squares = list(map(lambda x: x ** 2, numbers))
print(squares)  # [1, 4, 9, 16, 25]

# FILTER — оставляет только элементы, удовлетворяющие условию
even_numbers = list(filter(lambda x: x % 2 == 0, numbers))
print(even_numbers)  # [2, 4]

# REDUCE — объединяет элементы в одно значение
sum_all = reduce(lambda acc, x: acc + x, numbers, 0)
print(sum_all)  # 15 (0 + 1 + 2 + 3 + 4 + 5)

product_all = reduce(lambda acc, x: acc * x, numbers, 1)
print(product_all)  # 120 (1 * 1 * 2 * 3 * 4 * 5)

4. Композиция функций (Function Composition)

Соединяем маленькие функции в большие.

# Маленькие функции, каждая решает одну задачу
def add_two(x: int) -> int:
    return x + 2

def multiply_three(x: int) -> int:
    return x * 3

def square(x: int) -> int:
    return x ** 2

# Композиция вручную
result = square(multiply_three(add_two(5)))
print(result)  # (5 + 2) * 3 ^ 2 = 441

# Композиция через helper
def compose(*functions):
    """Объединяет функции слева направо"""
    def composed(x):
        for func in functions:
            x = func(x)
        return x
    return composed

pipeline = compose(add_two, multiply_three, square)
print(pipeline(5))  # 441

# или используем library
from functools import reduce
from operator import __rshift__  # для pipe operator

def pipe(*functions):
    return lambda x: reduce(lambda v, f: f(v), functions, x)

pipeline = pipe(add_two, multiply_three, square)
print(pipeline(5))  # 441

5. Избегаем циклов, используем рекурсию

# ПЛОХО — оперативный цикл, использует состояние
def sum_imperative(numbers: list) -> int:
    total = 0
    for num in numbers:
        total += num  # Изменяем состояние
    return total

# ХОРОШО — функциональная рекурсия
def sum_functional(numbers: list, index: int = 0, acc: int = 0) -> int:
    if index == len(numbers):
        return acc
    # Новый acc — это новое состояние, исходное не меняется
    return sum_functional(numbers, index + 1, acc + numbers[index])

print(sum_functional([1, 2, 3, 4, 5]))  # 15

6. Higher-Order Functions (функции высшего порядка)

# Функция, которая работает с другими функциями
def with_logging(func):
    """Декоратор — функция высшего порядка"""
    def wrapper(*args, **kwargs):
        print(f"Вызов {func.__name__}")
        result = func(*args, **kwargs)
        print(f"Результат: {result}")
        return result
    return wrapper

@with_logging
def multiply(a: int, b: int) -> int:
    return a * b

multiply(3, 4)
# Вывод:
# Вызов multiply
# Результат: 12

# Partial application — сохраняем часть аргументов
from functools import partial

def power(base: int, exponent: int) -> int:
    return base ** exponent

square = partial(power, exponent=2)
cube = partial(power, exponent=3)

print(square(5))  # 25
print(cube(5))    # 125

Пример: построение pipeline обработки данных

from functools import reduce
from typing import List, Callable, TypeVar

T = TypeVar('T')

# Маленькие, чистые функции
def parse_user(user_str: str) -> dict:
    """Преобразует строку в словарь"""
    name, age = user_str.split(',')
    return {"name": name, "age": int(age)}

def filter_adults(users: List[dict]) -> List[dict]:
    """Оставляет только взрослых (18+)"""
    return [u for u in users if u["age"] >= 18]

def sort_by_age(users: List[dict]) -> List[dict]:
    """Сортирует по возрасту"""
    return sorted(users, key=lambda u: u["age"])

def format_output(users: List[dict]) -> str:
    """Форматирует для вывода"""
    return "\n".join(
        f"{u['name']}: {u['age']} лет" for u in users
    )

# Составляем pipeline
raw_data = ["Alice,25", "Bob,17", "Charlie,30", "Diana,16"]

users = list(map(parse_user, raw_data))
result = format_output(sort_by_age(filter_adults(users)))
print(result)
# Вывод:
# Alice: 25 лет
# Charlie: 30 лет

Сравнение: Imperative vs Functional

# IMPERATIVE — что делать (HOW)
def process_orders_imperative(orders):
    expensive = []
    for order in orders:
        if order['total'] > 1000:
            expensive.append({
                'id': order['id'],
                'total': order['total'] * 0.9  # скидка 10%
            })
    return expensive

# FUNCTIONAL — что хочу (WHAT)
def process_orders_functional(orders):
    return list(
        map(
            lambda o: {'id': o['id'], 'total': o['total'] * 0.9},
            filter(lambda o: o['total'] > 1000, orders)
        )
    )

# или ещё более декларативно
from dataclasses import dataclass
from typing import List

@dataclass
class Order:
    id: int
    total: float

expensive_orders = (
    orders
    | filter(lambda o: o.total > 1000)
    | map(lambda o: Order(o.id, o.total * 0.9))
)

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

  1. Тестируемость — чистые функции легко тестировать
  2. Параллелизм — нет состояния, можно безопасно распараллелить
  3. Отладка — легче найти ошибку, если функция не зависит от контекста
  4. Переиспользование — маленькие функции легко комбинировать
  5. Понятность — код описывает ЧТО нужно, а не КАК это делать

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

Python — это мультипарадигменный язык, поддерживает оба стиля:

  • list comprehensions — функциональный стиль
  • map, filter, reduce — функциональные инструменты
  • lambda функции — анонимные функции
  • Но также поддерживает классический императивный стиль

Вывод

Функциональное программирование — это не «лучше или хуже», а другой подход к решению задач. Главная идея: разбивай программу на маленькие чистые функции, минимизируй побочные эффекты, композируй функции в большие системы. Это делает код более понятным, тестируемым и масштабируемым.

Какая основная идея функционального программирования? | PrepBro