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

Что такое чистая функция?

2.0 Middle🔥 181 комментариев
#Stream API и функциональное программирование

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

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

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

Чистая функция

Чистая функция — это функция, которая для одного и того же набора входных параметров всегда возвращает одно и то же значение и не имеет видимых побочных эффектов. Это фундаментальная концепция функционального программирования, которая делает код предсказуемым, тестируемым и параллелизируемым.

Определение чистой функции

Чистая функция должна соответствовать двум критериям:

  1. Детерминированность — для одного и того же входа функция всегда возвращает один и тот же выход
  2. Отсутствие побочных эффектов — функция не изменяет внешнее состояние и не имеет наблюдаемых эффектов кроме возврата значения

Примеры чистых функций

public class PureFunctions {
    // ЧИСТАЯ функция: математическая операция
    public static int add(int a, int b) {
        return a + b;
    }
    
    // ЧИСТАЯ функция: проверка условия
    public static boolean isEven(int number) {
        return number % 2 == 0;
    }
    
    // ЧИСТАЯ функция: преобразование строки
    public static String toUpperCase(String text) {
        return text.toUpperCase();
    }
    
    // ЧИСТАЯ функция: факториал
    public static int factorial(int n) {
        if (n <= 1) {
            return 1;
        }
        return n * factorial(n - 1);
    }
    
    // ЧИСТАЯ функция: средн. арифметическое
    public static double average(int[] numbers) {
        int sum = 0;
        for (int num : numbers) {
            sum += num;
        }
        return numbers.length > 0 ? (double) sum / numbers.length : 0;
    }
    
    public static void main(String[] args) {
        // Вызовы чистой функции всегда дают одинаковый результат
        System.out.println(add(3, 5));      // 8
        System.out.println(add(3, 5));      // 8
        System.out.println(add(3, 5));      // 8
        
        System.out.println(isEven(4));      // true
        System.out.println(isEven(4));      // true
        
        System.out.println(factorial(5));   // 120
        System.out.println(factorial(5));   // 120
    }
}

Примеры нечистых функций

public class ImpureFunctions {
    private static int counter = 0;
    
    // НЕЧИСТАЯ: зависит от внешнего состояния
    public static int incrementCounter() {
        return ++counter;  // Зависит от переменной counter
    }
    
    // НЕЧИСТАЯ: побочный эффект (печать)
    public static void printAdd(int a, int b) {
        System.out.println(a + b);  // Побочный эффект
    }
    
    // НЕЧИСТАЯ: использует текущее время
    public static long getCurrentTime() {
        return System.currentTimeMillis();  // Разные результаты при каждом вызове
    }
    
    // НЕЧИСТАЯ: изменяет объект
    public static void incrementAge(Person person) {
        person.setAge(person.getAge() + 1);  // Побочный эффект: изменение
    }
    
    // НЕЧИСТАЯ: обращается к БД
    public static User getUserFromDatabase(int id) {
        // Зависит от состояния БД, может выбросить исключение
        return database.query("SELECT * FROM users WHERE id = " + id);
    }
    
    // НЕЧИСТАЯ: генерирует случайное число
    public static int getRandomNumber() {
        return new java.util.Random().nextInt(100);
    }
}

class Person {
    private int age;
    
    public int getAge() {
        return age;
    }
    
    public void setAge(int age) {
        this.age = age;
    }
}

Сравнение: нечистая vs чистая функция

public class ComparisonExample {
    // НЕЧИСТАЯ ВЕРСИЯ
    private static int total = 0;
    
    public static void addToTotalImpure(int value) {
        total += value;  // Изменяет глобальное состояние
        System.out.println("Total: " + total);  // Побочный эффект: печать
    }
    
    // ЧИСТАЯ ВЕРСИЯ
    public static int calculateSum(int current, int value) {
        return current + value;  // Только вычисление, без побочных эффектов
    }
    
    public static void main(String[] args) {
        // Нечистая функция
        System.out.println("=== Impure ===");
        addToTotalImpure(5);   // Изменяет глобальное состояние, печатает
        addToTotalImpure(3);   // Зависит от предыдущего вызова
        addToTotalImpure(2);   // Результат зависит от истории вызовов
        
        // Чистая функция
        System.out.println("\n=== Pure ===");
        int result1 = calculateSum(0, 5);
        int result2 = calculateSum(result1, 3);
        int result3 = calculateSum(result2, 2);
        System.out.println("Result: " + result3);  // 10
    }
}

Характеристики чистых функций

1. Идентичные входы = идентичные выходы

public class DeterministicExample {
    // ЧИСТАЯ: всегда возвращает одинаковый результат
    public static int multiply(int a, int b) {
        return a * b;
    }
    
    public static void main(String[] args) {
        System.out.println(multiply(5, 3));  // 15
        System.out.println(multiply(5, 3));  // 15
        System.out.println(multiply(5, 3));  // 15
    }
}

2. Нет зависимостей от внешнего состояния

public class NoExternalState {
    private static double exchangeRate = 1.5;  // Глобальное состояние
    
    // НЕЧИСТАЯ: зависит от exchangeRate
    public static double convertCurrency(double amount) {
        return amount * exchangeRate;
    }
    
    // ЧИСТАЯ: передаём rate как параметр
    public static double convertCurrencyPure(double amount, double rate) {
        return amount * rate;
    }
}

3. Нет изменения передаваемых объектов

public class NoObjectMutation {
    // НЕЧИСТАЯ: изменяет переданный список
    public static void sortImpure(java.util.List<Integer> list) {
        java.util.Collections.sort(list);  // Изменяет исходный список
    }
    
    // ЧИСТАЯ: возвращает новый отсортированный список
    public static java.util.List<Integer> sortPure(java.util.List<Integer> list) {
        return list.stream()
                .sorted()
                .collect(java.util.stream.Collectors.toList());
    }
    
    public static void main(String[] args) {
        java.util.List<Integer> numbers = new java.util.ArrayList<>(java.util.Arrays.asList(3, 1, 2));
        java.util.List<Integer> sorted = sortPure(numbers);
        
        System.out.println("Original: " + numbers);  // [3, 1, 2] — не изменилась
        System.out.println("Sorted: " + sorted);     // [1, 2, 3]
    }
}

4. Нет побочных эффектов

public class NoSideEffects {
    // НЕЧИСТАЯ: множество побочных эффектов
    public static double calculateTotalImpure(java.util.List<Item> items) {
        System.out.println("Starting calculation");  // Побочный эффект 1
        double total = 0;
        for (Item item : items) {
            total += item.getPrice();
            item.setProcessed(true);  // Побочный эффект 2: изменение объекта
        }
        java.util.logging.Logger.getLogger("App").info("Total: " + total);  // Побочный эффект 3
        return total;
    }
    
    // ЧИСТАЯ: только вычисление
    public static double calculateTotalPure(java.util.List<Item> items) {
        return items.stream()
                .mapToDouble(Item::getPrice)
                .sum();
    }
}

class Item {
    private double price;
    private boolean processed;
    
    public Item(double price) {
        this.price = price;
    }
    
    public double getPrice() {
        return price;
    }
    
    public void setProcessed(boolean processed) {
        this.processed = processed;
    }
}

Преимущества чистых функций

  • Тестируемость — легко тестировать, нужны только входные параметры и ожидаемый результат
  • Отладка — отсутствие побочных эффектов упрощает отладку
  • Повторное использование — чистые функции легче использовать повторно
  • Параллелизм — можно безопасно вызывать параллельно без синхронизации
  • Кеширование — результаты можно кешировать без опасений
  • Рассуждения о коде — проще понять и предсказать поведение
  • Оптимизация — компилятор может оптимизировать чистые функции

Пример: рефакторинг нечистой функции в чистую

import java.util.*;

public class RefactoringExample {
    // НЕЧИСТАЯ версия
    private static List<String> processedUsers = new ArrayList<>();
    
    public static double calculateAverageAgeImpure(List<User> users) {
        processedUsers.clear();  // Побочный эффект
        double sum = 0;
        for (User user : users) {
            if (user.getAge() >= 18) {
                sum += user.getAge();
                processedUsers.add(user.getName());  // Побочный эффект
            }
        }
        return processedUsers.isEmpty() ? 0 : sum / processedUsers.size();
    }
    
    // ЧИСТАЯ версия
    public static double calculateAverageAgePure(List<User> users) {
        return users.stream()
                .filter(user -> user.getAge() >= 18)
                .mapToDouble(User::getAge)
                .average()
                .orElse(0.0);
    }
    
    public static void main(String[] args) {
        List<User> users = Arrays.asList(
                new User("John", 25),
                new User("Jane", 30),
                new User("Bob", 17)
        );
        
        double average = calculateAverageAgePure(users);
        System.out.println("Average age: " + average);  // 27.5
    }
}

class User {
    private String name;
    private int age;
    
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    public String getName() {
        return name;
    }
    
    public int getAge() {
        return age;
    }
}

Best Practices

  • Стремись писать чистые функции везде, где это возможно
  • Передавай все необходимые параметры как аргументы
  • Возвращай результаты вместо изменения состояния
  • Избегай побочных эффектов (печать, запись в файл, БД)
  • Используй неизменяемые данные
  • Обработку побочных эффектов отдели в отдельные функции/слои
  • Используй Stream API и функциональные интерфейсы

Чистые функции — это скелет хорошего, масштабируемого кода. Их использование приводит к более надёжному и поддерживаемому программному обеспечению

Что такое чистая функция? | PrepBro