Что такое функциональное программирование (ФП) и его основные концепции?

Ответ

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

Ключевые концепции:

Концепция Описание Пример в Java (с использованием Stream API)
Чистые функции Функция, которая для одинаковых входных данных всегда возвращает одинаковый результат и не имеет побочных эффектов. x -> x * x (лямбда для возведения в квадрат)
Неизменяемость Состояние объекта не может быть изменено после создания. Использование final переменных и коллекций из java.util.Collections.unmodifiableList или List.of().
Функции высшего порядка Функции, которые могут принимать функции как аргументы или возвращать их как результат. Методы stream.map(), stream.filter(), stream.reduce().
Рекурсия Часто используется вместо циклов для итерации. Вычисление факториала: int fact(int n) { return n <= 1 ? 1 : n * fact(n-1); }
Ссылочная прозрачность Возможность замены выражения его значением без изменения поведения программы. Свойство чистых функций.

Практический пример на Java:

import java.util.List;
import java.util.stream.Collectors;

public class FPExample {
    public static void main(String[] args) {
        List<Integer> numbers = List.of(1, 2, 3, 4, 5);

        // Функциональный стиль: фильтрация и преобразование
        List<Integer> result = numbers.stream()
            .filter(n -> n % 2 != 0)      // Чистая функция: оставить нечётные
            .map(n -> n * n)              // Чистая функция: возвести в квадрат
            .collect(Collectors.toList()); // Создание нового списка

        System.out.println(result); // Вывод: [1, 9, 25]
        // Исходный список 'numbers' остался неизменным.
    }
}

Зачем это нужно в Java? Даже в объектно-ориентированном языке элементы ФП (лямбды, Stream API) позволяют писать более выразительный, тестируемый и безопасный для многопоточности код, минимизируя изменяемое состояние.

Ответ 18+ 🔞

А, функциональное программирование, говоришь? Ну, это такая штука, где ты, сука, пытаешься писать код так, будто ты не мудак, который постоянно всё ломает вокруг себя. Представь: ты не можешь взять и просто так, с бухты-барахты, изменить переменную. Ни хуя! Всё должно быть свято, блядь, неизменно, как клятва верности пьяного моряка, которую он забывает наутро.

Вот смотри, ключевые идеи, чтобы не облажаться:

Чистые функции — это святое, блядь. Как водка из морозилки: один и тот же вход — один и тот же выхлоп, и никаких побочных эффектов, типа «ой, а я заодно глобальную переменную переписал, чих-пых тебя в сраку». Пример? x -> x * x. Возвёл в квадрат и пошёл на хуй. Красота.

Неизменяемость — это когда ты создал объект, и он теперь как семейная реликвия: трогать нельзя, только любоваться. В Java это final да List.of(), чтобы какой-нибудь охуевший коллега не решил твой список посреди работы «немного подправить».

Функции высшего порядка — звучит сложно, а на деле это просто функции, которые жрут другие функции или срут ими. В Java Stream API это повсюду: map, filter, reduce. Подсовываешь им лямбду, а они тебе новый поток данных выдают. Магия, блядь, только без ёбнутой магии с глобальным состоянием.

Рекурсия — это когда вместо нормального цикла ты вызываешь функцию саму из себя, пока стек не взвоет «мама, я в аду!». Но зато красиво, математично, ёпта. Факториал там всякий.

А теперь, чтобы не быть пустым трёпом, вот тебе живой пример, как это в Java выглядит. Смотри, не моргай, а то пропустишь:

import java.util.List;
import java.util.stream.Collectors;

public class FPExample {
    public static void main(String[] args) {
        List<Integer> numbers = List.of(1, 2, 3, 4, 5);

        // Функциональный стиль: фильтрация и преобразование
        List<Integer> result = numbers.stream()
            .filter(n -> n % 2 != 0)      // Отфильтровали чётные, нахуй они не нужны
            .map(n -> n * n)              // Каждый оставшийся возвели в квадрат
            .collect(Collectors.toList()); // Собрали в новый список

        System.out.println(result); // Вывод: [1, 9, 25]
        // А исходный список 'numbers'? Да жив-здоров, не тронут, как девичья честь в монастыре.
    }
}

Зачем этот цирк в Java, спросишь? А затем, чувак, чтобы код был предсказуемым, как удар током от оголённого провода. Его легче тестировать, отлаживать, и в многопоточке он не устроит такой пиздец, когда десять потоков одновременно пытаются запихать свои руки в одну общую переменную. Это как перестать, блядь, строить дом из песка во время урагана. Попробуй, может, понравится.