Skip to content
IRC-CodingIRC-Coding
Functional ProgrammingLambdaFunctional InterfacesHigher-Order FunctionsJava 8Stream APIPure FunctionsImmutabilityпрограммированиеязыки программированияфункциональное программирование

Функциональное программирование: Lambda и Higher-Order Functions

Lambda, Functional Interfaces и Higher-Order Functions в Java 8+ и Python с практическими примерами.

S

schutzgeist

17 min read
Функциональное программирование: Lambda и Higher-Order Functions

Функциональное программирование: Lambda, Functional Interfaces и Higher-Order Functions

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

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

Ключевые принципы

  • Pure Functions: функции без побочных эффектов
  • Immutability: неизменяемые структуры данных
  • First-Class Functions: функции как значения
  • Higher-Order Functions: функции, принимающие другие функции в качестве аргументов
  • Function Composition: комбинирование функций

Чистые и нечистые функции

public class FunctionExamples {
    
    // Чистая функция - никаких побочных эффектов
    public static int add(int a, int b) {
        return a + b;
        // Всегда возвращает один и тот же результат для одних и тех же входных данных
        // Нет побочных эффектов
    }
    
    // Нечистая функция - с побочными эффектами
    private static int counter = 0;
    
    public static int addToCounter(int value) {
        counter += value; // Побочный эффект: изменяет глобальное состояние
        return counter;
        // Для одного и того же входного значения может вернуть разные результаты
    }
    
    // Чистая функция для обработки строк
    public static String capitalize(String input) {
        if (input == null) return null;
        return input.toUpperCase();
    }
    
    // Нечистая функция с внешней зависимостью
    public static String getCurrentTimeFormatted() {
        return DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(LocalDateTime.now());
        // Зависит от текущего времени
    }
}

Lambda-выражения в Java

Синтаксис и основы

public class LambdaBasics {
    
    // Традиционный подход с анонимным классом
    public static void traditionalApproach() {
        Runnable runnable1 = new Runnable() {
            @Override
            public void run() {
                System.out.println("Traditional approach");
            }
        };
        runnable1.run();
    }
    
    // Lambda-выражение
    public static void lambdaApproach() {
        Runnable runnable2 = () -> System.out.println("Lambda approach");
        runnable2.run();
    }
    
    // Lambda с разными синтаксическими вариантами
    public static void lambdaSyntaxExamples() {
        
        // Без параметров
        Runnable noParams = () -> System.out.println("No parameters");
        
        // Один параметр
        Consumer<String> oneParam = s -> System.out.println("Parameter: " + s);
        
        // Один параметр с явным типом
        Consumer<String> oneParamTyped = (String s) -> System.out.println("Typed: " + s);
        
        // Несколько параметров
        BinaryOperator<Integer> twoParams = (a, b) -> a + b;
        
        // Несколько параметров с типами
        BinaryOperator<Integer> twoParamsTyped = (Integer a, Integer b) -> a + b;
        
        // Несколько строк кода
        Predicate<String> multiLine = s -> {
            String trimmed = s.trim();
            return trimmed.length() > 5 && trimmed.startsWith("A");
        };
        
        // Использование
        noParams.run();
        oneParam.accept("Hello");
        System.out.println(twoParams.apply(5, 3));
        System.out.println(multiLine.test("Hello World"));
    }
}

Functional Interfaces

// Пользовательские Functional Interfaces
@FunctionalInterface
public interface StringProcessor {
    String process(String input);
    
    // Может содержать методы по умолчанию
    default String processAndLog(String input) {
        String result = process(input);
        System.out.println("Processed: " + result);
        return result;
    }
    
    // Может содержать статические методы
    static StringProcessor toUpperCase() {
        return String::toUpperCase;
    }
}

@FunctionalInterface
public interface TriFunction<T, U, V, R> {
    R apply(T t, U u, V v);
}

// Использование Functional Interfaces
public class FunctionalInterfaceExamples {
    
    public static void demonstrateInterfaces() {
        
        // Predicate - логический тест
        Predicate<Integer> isEven = n -> n % 2 == 0;
        Predicate<String> isLong = s -> s.length() > 10;
        
        System.out.println("4 is even: " + isEven.test(4));
        System.out.println("Hello is long: " + isLong.test("Hello"));
        
        // Consumer - обрабатывает значения
        Consumer<String> printer = System.out::println;
        Consumer<List<Integer>> listPrinter = list -> list.forEach(System.out::println);
        
        printer.accept("Hello World");
        listPrinter.accept(Arrays.asList(1, 2, 3, 4, 5));
        
        // Supplier - предоставляет значения
        Supplier<Double> randomSupplier = Math::random;
        Supplier<LocalDateTime> nowSupplier = LocalDateTime::now;
        
        System.out.println("Random: " + randomSupplier.get());
        System.out.println("Now: " + nowSupplier.get());
        
        // Function - преобразование значений
        Function<String, Integer> stringLength = String::length;
        Function<Integer, String> intToString = Object::toString;
        
        System.out.println("Length of Hello: " + stringLength.apply("Hello"));
        System.out.println("String of 42: " + intToString.apply(42));
        
        // UnaryOperator - специальный случай Function
        UnaryOperator<String> upperCase = String::toUpperCase;
        UnaryOperator<Integer> square = n -> n * n;
        
        System.out.println("Upper: " + upperCase.apply("hello"));
        System.out.println("Square: " + square.apply(5));
        
        // BinaryOperator - два параметра
        BinaryOperator<Integer> add = Integer::sum;
        BinaryOperator<String> concat = String::concat;
        
        System.out.println("Add: " + add.apply(3, 7));
        System.out.println("Concat: " + concat.apply("Hello", " World"));
        
        // Пользовательский Functional Interface
        StringProcessor reverser = s -> new StringBuilder(s).reverse().toString();
        StringProcessor prefixAdder = s -> "Prefix: " + s;
        
        System.out.println("Reverse: " + reverser.process("Hello"));
        System.out.println("Prefix: " + prefixAdder.process("World"));
        
        // Пользовательский TriFunction
        TriFunction<Integer, Integer, Integer, Integer> sumThree = (a, b, c) -> a + b + c;
        System.out.println("Sum of 1,2,3: " + sumThree.apply(1, 2, 3));
    }
}

Higher-Order Functions

Функции как параметры

public class HigherOrderFunctions {
    
    // Функция, которая принимает функцию в качестве параметра
    public static <T, R> List<R> map(List<T> list, Function<T, R> mapper) {
        List<R> result = new ArrayList<>();
        for (T item : list) {
            result.add(mapper.apply(item));
        }
        return result;
    }
    
    // Функция, которая принимает предикат и фильтрует список
    public static <T> List<T> filter(List<T> list, Predicate<T> predicate) {
        List<T> result = new ArrayList<>();
        for (T item : list) {
            if (predicate.test(item)) {
                result.add(item);
            }
        }
        return result;
    }
    
    // Функция, которая принимает функцию для редукции
    public static <T> T reduce(List<T> list, T identity, BinaryOperator<T> accumulator) {
        T result = identity;
        for (T item : list) {
            result = accumulator.apply(result, item);
        }
        return result;
    }
    
    // Функция, которая возвращает функцию (замыкание)
    public static Function<Integer, Integer> createMultiplier(int multiplier) {
        return number -> number * multiplier;
    }
    
    // Функция с несколькими функциями в качестве параметров
    public static <T> List<T> processList(
            List<T> list,
            Predicate<T> filter,
            Function<T, T> mapper,
            Consumer<T> consumer) {
        
        List<T> result = new ArrayList<>();
        for (T item : list) {
            if (filter.test(item)) {
                T processed = mapper.apply(item);
                consumer.accept(processed);
                result.add(processed);
            }
        }
        return result;
    }
    
    // Демонстрация
    public static void demonstrateHigherOrderFunctions() {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        
        // Map: возведение чисел в квадрат
        List<Integer> squares = map(numbers, n -> n * n);
        System.out.println("Squares: " + squares);
        
        // Filter: только четные числа
        List<Integer> evens = filter(numbers, n -> n % 2 == 0);
        System.out.println("Evens: " + evens);
        
        // Reduce: вычисление суммы
        Integer sum = reduce(numbers, 0, Integer::sum);
        System.out.println("Sum: " + sum);
        
        // Замыкание: создание функции-удвоителя
        Function<Integer, Integer> doubler = createMultiplier(2);
        Function<Integer, Integer> tripler = createMultiplier(3);
        
        System.out.println("Double of 5: " + doubler.apply(5));
        System.out.println("Triple of 5: " + tripler.apply(5));
        
        // Сложная обработка
        List<String> words = Arrays.asList("apple", "banana", "cherry", "date", "elderberry");
        
        List<String> processed = processList(
            words,
            s -> s.length() > 5,     // Filter: только длинные слова
            String::toUpperCase,     // Map: в верхний регистр
            System.out::println      // Consumer: вывод
        );
        
        System.out.println("Processed: " + processed);
    }
}

Композиция функций

public class FunctionComposition {
    
    // Вспомогательные методы для композиции
    public static <T, U, R> Function<T, R> compose(Function<U, R> f, Function<T, U> g) {
        return x -> f.apply(g.apply(x));
    }
    
    public static <T> Function<T, T> compose(Function<T, T>... functions) {
        return Arrays.stream(functions)
            .reduce(Function.identity(), Function::andThen);
    }
    
    // Пример композиции
    public static void demonstrateComposition() {
        // Отдельные функции
        Function<String, String> addPrefix = s -> "Hello " + s;
        Function<String, String> addSuffix = s -> s + "!";
        Function<String, String> toUpperCase = String::toUpperCase;
        
        // Объединение функций
        Function<String, String> greet = compose(addSuffix, addPrefix);
        Function<String, String> greetLoud = compose(toUpperCase, greet);
        
        System.out.println(greet.apply("World"));        // Hello World!
        System.out.println(greetLoud.apply("World"));    // HELLO WORLD!
        
        // С andThen (обратный порядок)
        Function<String, String> greetAndThenUpper = addPrefix.andThen(toUpperCase).andThen(addSuffix);
        System.out.println(greetAndThenUpper.apply("World")); // HELLO WORLD!
        
        // Математические примеры
        Function<Double, Double> multiplyBy2 = x -> x * 2;
        Function<Double, Double> add3 = x -> x + 3;
        Function<Double, Double> square = x -> x * x;
        
        // (x * 2 + 3)²
        Function<Double, Double> complexOperation = compose(square, compose(add3, multiplyBy2));
        System.out.println("Complex operation (5): " + complexOperation.apply(5.0)); // ((5*2)+3)² = 13² = 169
    }
}

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

Обработка потоков

public class StreamFunctionalProgramming {
    
    public static void demonstrateStreamProcessing() {
        List<Person> people = Arrays.asList(
            new Person("Alice", 25, "Engineering"),
            new Person("Bob", 30, "Marketing"),
            new Person("Charlie", 35, "Engineering"),
            new Person("Diana", 28, "HR"),
            new Person("Eve", 32, "Engineering")
        );
        
        // Сложная обработка потока
        List<String> result = people.stream()
            .filter(p -> p.getAge() >= 30)                    // Фильтр: возраст >= 30
            .filter(p -> p.getDepartment().equals("Engineering")) // Фильтр: Engineering
            .map(Person::getName)                            // Map: только имена
            .map(String::toUpperCase)                        // Map: заглавные буквы
            .sorted()                                         // Сортировка
            .collect(Collectors.toList());                    // Сбор результата
        
        System.out.println("Filtered and mapped: " + result);
        
        // Редукция со специализированными операторами
        double totalAge = people.stream()
            .mapToInt(Person::getAge)
            .sum();
        
        System.out.println("Total age: " + totalAge);
        
        // Группировка
        Map<String, List<Person>> byDepartment = people.stream()
            .collect(Collectors.groupingBy(Person::getDepartment));
        
        System.out.println("By department: " + byDepartment);
        
        // Разделение
        Map<Boolean, List<Person>> byAge = people.stream()
            .collect(Collectors.partitioningBy(p -> p.getAge() >= 30));
        
        System.out.println("By age >= 30: " + byAge);
        
        // Пользовательский коллектор
        Map<String, Double> avgAgeByDept = people.stream()
            .collect(Collectors.groupingBy(
                Person::getDepartment,
                Collectors.averagingInt(Person::getAge)
            ));
        
        System.out.println("Average age by department: " + avgAgeByDept);
    }
    
    // Класс Person для примеров
    public static class Person {
        private String name;
        private int age;
        private String department;
        
        public Person(String name, int age, String department) {
            this.name = name;
            this.age = age;
            this.department = department;
        }
        
        // Геттеры
        public String getName() { return name; }
        public int getAge() { return age; }
        public String getDepartment() { return department; }
        
        @Override
        public String toString() {
            return String.format("%s (%d, %s)", name, age, department);
        }
    }
}

Ленивые вычисления со Stream

public class LazyEvaluation {
    
    public static void demonstrateLazyEvaluation() {
        // Бесконечный поток, работающий благодаря ленивым вычислениям
        Stream<Integer> infiniteStream = Stream.iterate(0, n -> n + 1);
        
        // Вычисляются только первые 10 элементов
        List<Integer> firstTen = infiniteStream
            .limit(10)
            .collect(Collectors.toList());
        
        System.out.println("First 10: " + firstTen);
        
        // Числа Фибоначчи с ленивыми вычислениями
        Stream<Long> fibonacci = Stream.generate(new FibonacciSupplier());
        
        List<Long> firstFibonacci = fibonacci
            .limit(10)
            .collect(Collectors.toList());
        
        System.out.println("First 10 Fibonacci: " + firstFibonacci);
        
        // Ленивые фильтр и map
        List<Integer> processed = Stream.iterate(1, n -> n + 1)
            .filter(LazyEvaluation::isPrime)      // Только простые числа
            .map(n -> n * n)                      // Возведение в квадрат
            .limit(5)                             // Только первые 5
            .collect(Collectors.toList());
        
        System.out.println("First 5 prime squares: " + processed);
    }
    
    private static boolean isPrime(int n) {
        if (n <= 1) return false;
        if (n == 2) return true;
        if (n % 2 == 0) return false;
        
        for (int i = 3; i * i <= n; i += 2) {
            if (n % i == 0) return false;
        }
        return true;
    }
    
    // Поставщик Фибоначчи
    private static class FibonacciSupplier implements Supplier<Long> {
        private long a = 0;
        private long b = 1;
        
        @Override
        public Long get() {
            long result = a;
            long next = a + b;
            a = b;
            b = next;
            return result;
        }
    }
}

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

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

from functools import reduce, partial
from typing import List, Callable, Any

# Lambda выражения
def lambda_examples():
    # Простые lambda функции
    square = lambda x: x ** 2
    add = lambda x, y: x + y
    is_even = lambda x: x % 2 == 0
    
    print(f"Square of 5: {square(5)}")
    print(f"Add 3 + 7: {add(3, 7)}")
    print(f"Is 4 even: {is_even(4)}")
    
    # Lambda со встроенными функциями
    numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    
    # Map с lambda
    squares = list(map(lambda x: x ** 2, numbers))
    print(f"Squares: {squares}")
    
    # Filter с lambda
    evens = list(filter(lambda x: x % 2 == 0, numbers))
    print(f"Evens: {evens}")
    
    # Reduce с lambda
    sum_all = reduce(lambda x, y: x + y, numbers)
    print(f"Sum: {sum_all}")
    
    # Сортировка с lambda
    words = ["apple", "banana", "cherry", "date"]
    sorted_by_length = sorted(words, key=lambda x: len(x))
    print(f"Sorted by length: {sorted_by_length}")

# Функции высшего порядка
def higher_order_functions():
    # Функция, принимающая функцию как параметр
    def apply_operation(numbers: List[int], operation: Callable[[int], int]) -> List[int]:
        return [operation(num) for num in numbers]
    
    # Функция, возвращающая функцию
    def create_multiplier(factor: int) -> Callable[[int], int]:
        return lambda x: x * factor
    
    # Использование
    numbers = [1, 2, 3, 4, 5]
    
    squares = apply_operation(numbers, lambda x: x ** 2)
    cubes = apply_operation(numbers, lambda x: x ** 3)
    
    print(f"Squares: {squares}")
    print(f"Cubes: {cubes}")
    
    # Замыкание
    doubler = create_multiplier(2)
    tripler = create_multiplier(3)
    
    print(f"Double of 5: {doubler(5)}")
    print(f"Triple of 5: {tripler(5)}")
    
    # Композиция функций
    def compose(f: Callable, g: Callable) -> Callable:
        return lambda x: f(g(x))
    
    add_one = lambda x: x + 1
    multiply_by_two = lambda x: x * 2
    
    add_then_multiply = compose(multiply_by_two, add_one)
    multiply_then_add = compose(add_one, multiply_by_two)
    
    print(f"Add then multiply (5): {add_then_multiply(5)}")  # (5 + 1) * 2 = 12
    print(f"Multiply then add (5): {multiply_then_add(5)}")  # (5 * 2) + 1 = 11

# Частичное применение
def partial_functions():
    def multiply(x: int, y: int) -> int:
        return x * y
    
    def power(base: int, exponent: int) -> int:
        return base ** exponent
    
    # Partial application
    double = partial(multiply, 2)
    triple = partial(multiply, 3)
    
    square = partial(power, 2)
    cube = partial(power, 3)
    
    print(f"Double of 5: {double(5)}")
    print(f"Triple of 5: {triple(5)}")
    print(f"Square of 5: {square(5)}")
    print(f"Cube of 5: {cube(5)}")

# Списковые выражения (функциональная альтернатива)
def list_comprehensions():
    numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    
    # Традиционный способ с map/filter
    squares_even = list(filter(lambda x: x % 2 == 0, map(lambda x: x ** 2, numbers)))
    
    # Со списковым выражением
    squares_even_comp = [x ** 2 for x in numbers if x % 2 == 0]
    
    print(f"Squares of evens (map/filter): {squares_even}")
    print(f"Squares of evens (comprehension): {squares_even_comp}")
    
    # Вложенные выражения
    matrix = [[i * j for j in range(1, 4)] for i in range(1, 4)]
    print(f"Multiplication table: {matrix}")
    
    # Выражение для словаря
    word_lengths = {word: len(word) for word in ["apple", "banana", "cherry"]}
    print(f"Word lengths: {word_lengths}")

# Декораторы (функции высшего порядка)
def decorators():
    def timing_decorator(func):
        import time
        def wrapper(*args, **kwargs):
            start = time.time()
            result = func(*args, **kwargs)
            end = time.time()
            print(f"{func.__name__} took {end - start:.4f} seconds")
            return result
        return wrapper
    
    def memoization_decorator(func):
        cache = {}
        def wrapper(*args):
            if args in cache:
                return cache[args]
            result = func(*args)
            cache[args] = result
            return result
        return wrapper
    
    @timing_decorator
    @memoization_decorator
    def fibonacci(n: int) -> int:
        if n <= 1:
            return n
        return fibonacci(n - 1) + fibonacci(n - 2)
    
    print(f"Fibonacci(10): {fibonacci(10)}")
    print(f"Fibonacci(15): {fibonacci(15)}")  # Быстрее благодаря мемоизации

# Запуск всех примеров
if __name__ == "__main__":
    print("=== Lambda Examples ===")
    lambda_examples()
    
    print("\n=== Higher-Order Functions ===")
    higher_order_functions()
    
    print("\n=== Partial Functions ===")
    partial_functions()
    
    print("\n=== List Comprehensions ===")
    list_comprehensions()
    
    print("\n=== Decorators ===")
    decorators()

Неизменяемость и чистые функции

Неизменяемые структуры данных

public class ImmutableDataStructures {
    
    // Неизменяемый класс Person
    public static final class Person {
        private final String name;
        private final int age;
        private final List<String> hobbies;
        
        public Person(String name, int age, List<String> hobbies) {
            this.name = Objects.requireNonNull(name);
            this.age = age;
            this.hobbies = List.copyOf(hobbies); // Defensive Copy
        }
        
        // Getter - без Setter!
        public String getName() { return name; }
        public int getAge() { return age; }
        public List<String> getHobbies() { return hobbies; }
        
        // Методы создают новые экземпляры
        public Person withAge(int newAge) {
            return new Person(this.name, newAge, this.hobbies);
        }
        
        public Person withName(String newName) {
            return new Person(newName, this.age, this.hobbies);
        }
        
        public Person addHobby(String hobby) {
            List<String> newHobbies = new ArrayList<>(this.hobbies);
            newHobbies.add(hobby);
            return new Person(this.name, this.age, newHobbies);
        }
        
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Person person = (Person) o;
            return age == person.age && 
                   Objects.equals(name, person.name) && 
                   Objects.equals(hobbies, person.hobbies);
        }
        
        @Override
        public int hashCode() {
            return Objects.hash(name, age, hobbies);
        }
        
        @Override
        public String toString() {
            return String.format("Person{name='%s', age=%d, hobbies=%s}", name, age, hobbies);
        }
    }
    
    // Функциональные операции над неизменяемыми данными
    public static void demonstrateImmutability() {
        List<Person> people = Arrays.asList(
            new Person("Alice", 25, Arrays.asList("reading", "swimming")),
            new Person("Bob", 30, Arrays.asList("gaming", "cooking")),
            new Person("Charlie", 35, Arrays.asList("music", "travel"))
        );
        
        // Исходный список остается неизменным
        List<Person> olderPeople = people.stream()
            .map(person -> person.withAge(person.getAge() + 1))
            .collect(Collectors.toList());
        
        System.out.println("Original: " + people);
        System.out.println("Aged by 1: " + olderPeople);
        
        // Функциональное преобразование
        List<String> hobbies = people.stream()
            .flatMap(person -> person.getHobbies().stream())
            .distinct()
            .sorted()
            .collect(Collectors.toList());
        
        System.out.println("All hobbies: " + hobbies);
    }
}

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

public class PureFunctions {
    
    // Чистая функция - без побочных эффектов
    public static int calculateArea(int width, int height) {
        if (width < 0 || height < 0) {
            throw new IllegalArgumentException("Dimensions must be positive");
        }
        return width * height;
    }
    
    // Чистая функция для обработки строк
    public static String formatFullName(String firstName, String lastName) {
        if (firstName == null || lastName == null) {
            return "";
        }
        return String.format("%s %s", 
            firstName.trim().toUpperCase(), 
            lastName.trim().toUpperCase()
        );
    }
    
    // Чистая функция для обработки списков
    public static List<Integer> filterAndSquare(List<Integer> numbers, Predicate<Integer> predicate) {
        return numbers.stream()
            .filter(predicate)
            .map(n -> n * n)
            .collect(Collectors.toList());
    }
    
    // Нечистая функция - для сравнения
    private static int counter = 0;
    
    public static int incrementCounter() {
        return counter++; // Побочный эффект!
    }
    
    // Демонстрация
    public static void demonstratePureFunctions() {
        // Чистая функция - всегда одинаковый результат
        int area1 = calculateArea(5, 3);
        int area2 = calculateArea(5, 3);
        System.out.println("Areas are equal: " + (area1 == area2)); // true
        
        // Чистая функция со строками
        String fullName1 = formatFullName("John", "Doe");
        String fullName2 = formatFullName("John", "Doe");
        System.out.println("Names are equal: " + fullName1.equals(fullName2)); // true
        
        // Чистая функция со списками
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        List<Integer> evenSquares = filterAndSquare(numbers, n -> n % 2 == 0);
        System.out.println("Even squares: " + evenSquares);
        
        // Нечистая функция - разные результаты
        int count1 = incrementCounter();
        int count2 = incrementCounter();
        System.out.println("Counts are different: " + (count1 != count2)); // true
    }
}

Монады и функциональные концепции

Optional Monad

public class MonadExamples {
    
    // Optional для безопасных операций с null
    public static void demonstrateOptional() {
        Optional<String> optional = Optional.of("Hello World");
        
        // Трансформация map
        Optional<Integer> length = optional.map(String::length);
        System.out.println("Length: " + length.orElse(0));
        
        // Фильтрация
        Optional<String> filtered = optional.filter(s -> s.length() > 5);
        System.out.println("Filtered: " + filtered.orElse("Too short"));
        
        // FlatMap для вложенных Optional
        Optional<String> upperCase = optional.flatMap(s -> 
            s.length() > 5 ? Optional.of(s.toUpperCase()) : Optional.empty()
        );
        System.out.println("Upper case: " + upperCase.orElse("Not available"));
        
        // Безопасная цепочка операций
        String result = Optional.ofNullable(getUser())
            .map(User::getAddress)
            .map(Address::getCity)
            .orElse("Unknown");
        
        System.out.println("City: " + result);
    }
    
    // Either Monad (упрощенная реализация)
    public static class Either<L, R> {
        private final L left;
        private final R right;
        
        private Either(L left, R right) {
            this.left = left;
            this.right = right;
        }
        
        public static <L, R> Either<L, R> left(L value) {
            return new Either<>(value, null);
        }
        
        public static <L, R> Either<L, R> right(R value) {
            return new Either<>(null, value);
        }
        
        public boolean isLeft() { return left != null; }
        public boolean isRight() { return right != null; }
        
        public L getLeft() { return left; }
        public R getRight() { return right; }
        
        public <T> Either<L, T> map(Function<R, T> mapper) {
            if (isRight()) {
                return Either.right(mapper.apply(right));
            }
            return Either.left(left);
        }
        
        public <T> Either<L, T> flatMap(Function<R, Either<L, T>> mapper) {
            if (isRight()) {
                return mapper.apply(right);
            }
            return Either.left(left);
        }
    }
    
    // Использование Either
    public static Either<String, Integer> parseNumber(String input) {
        try {
            return Either.right(Integer.parseInt(input));
        } catch (NumberFormatException e) {
            return Either.left("Invalid number: " + input);
        }
    }
    
    public static void demonstrateEither() {
        Either<String, Integer> result1 = parseNumber("123");
        Either<String, Integer> result2 = parseNumber("abc");
        
        result1.map(n -> n * 2)
               .map(Object::toString)
               .ifRight(System.out::println)
               .ifLeft(System.err::println);
        
        result2.map(n -> n * 2)
               .map(Object::toString)
               .ifRight(System.out::println)
               .ifLeft(System.err::println);
    }
    
    // Вспомогательные методы для Either
    private static <T> void ifRight(Either<String, T> either, Consumer<T> consumer) {
        if (either.isRight()) {
            consumer.accept(either.getRight());
        }
    }
    
    private static <T> void ifLeft(Either<String, T> either, Consumer<String> consumer) {
        if (either.isLeft()) {
            consumer.accept(either.getLeft());
        }
    }
    
    // Вспомогательные классы для примеров
    private static User getUser() {
        return new User("John", new Address("123 Main St", "New York"));
    }
    
    private static class User {
        private String name;
        private Address address;
        
        public User(String name, Address address) {
            this.name = name;
            this.address = address;
        }
        
        public Address getAddress() { return address; }
    }
    
    private static class Address {
        private String street;
        private String city;
        
        public Address(String street, String city) {
            this.street = street;
            this.city = city;
        }
        
        public String getCity() { return city; }
    }
}

Производительность и лучшие практики

Производительность функционального подхода

public class FunctionalPerformance {
    
    // Традиционный цикл
    public static int traditionalSum(List<Integer> numbers) {
        int sum = 0;
        for (int num : numbers) {
            sum += num;
        }
        return sum;
    }
    
    // Функциональный вариант со Stream
    public static int functionalSum(List<Integer> numbers) {
        return numbers.stream().reduce(0, Integer::sum);
    }
    
    // Parallel Stream для больших объёмов данных
    public static int parallelSum(List<Integer> numbers) {
        return numbers.parallelStream().reduce(0, Integer::sum);
    }
    
    // Сравнение производительности
    public static void performanceComparison() {
        List<Integer> numbers = IntStream.range(0, 1_000_000)
            .boxed()
            .collect(Collectors.toList());
        
        // Прогрев
        traditionalSum(numbers);
        functionalSum(numbers);
        parallelSum(numbers);
        
        // Бенчмарк
        long start = System.nanoTime();
        int result1 = traditionalSum(numbers);
        long traditionalTime = System.nanoTime() - start;
        
        start = System.nanoTime();
        int result2 = functionalSum(numbers);
        long functionalTime = System.nanoTime() - start;
        
        start = System.nanoTime();
        int result3 = parallelSum(numbers);
        long parallelTime = System.nanoTime() - start;
        
        System.out.println("Results equal: " + (result1 == result2 && result2 == result3));
        System.out.println("Traditional: " + (traditionalTime / 1_000_000) + " ms");
        System.out.println("Functional: " + (functionalTime / 1_000_000) + " ms");
        System.out.println("Parallel: " + (parallelTime / 1_000_000) + " ms");
    }
    
    // Лучшие практики
    public static void bestPractices() {
        List<String> words = Arrays.asList("apple", "banana", "cherry", "date", "elderberry");
        
        // 1. Method References вместо Lambda когда возможно
        List<Integer> lengths1 = words.stream()
            .map(s -> s.length())           // Lambda
            .collect(Collectors.toList());
        
        List<Integer> lengths2 = words.stream()
            .map(String::length)            // Method Reference
            .collect(Collectors.toList());
        
        // 2. Primitive Streams для оптимизации
        int sum = words.stream()
            .mapToInt(String::length)       // IntStream вместо Stream<Integer>
            .sum();
        
        // 3. Использование ленивых вычислений
        Optional<String> firstLong = words.stream()
            .filter(s -> s.length() > 5)
            .findFirst();                    // Останавливается после первого совпадения
        
        // 4. Предпочитайте неизменяемые операции
        List<String> processed = words.stream()
            .map(String::toUpperCase)
            .filter(s -> s.startsWith("A"))
            .collect(Collectors.toList());  // Новый список вместо модификации
        
        System.out.println("Best practices completed");
    }
}

Концепции, проверяемые на экзамене

Ключевые функциональные концепции

  1. Pure Functions: отсутствие побочных эффектов, детерминированность
  2. Immutability: неизменяемые структуры данных
  3. Higher-Order Functions: функции как параметры или возвращаемые значения
  4. Function Composition: комбинирование функций
  5. Lazy Evaluation: отложенное вычисление
  6. Monads: Optional, Either, Stream

Типичные экзаменационные задачи

  1. Реализуйте чистую функцию
  2. Объясните Function Composition
  3. Сравните императивный и функциональный подходы
  4. Реализуйте Higher-Order Function
  5. Опишите преимущества функционального программирования

Итоги

Функциональное программирование даёт множество преимуществ:

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

Сочетание объектно-ориентированного и функционального подходов позволяет создавать надёжные, масштабируемые и легко поддерживаемые архитектуры.

Рекомендуемая литература

Keine Bücher für Kategorie "programming-languages" gefunden.

Другие статьи по функциональному программированию

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

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

Назад к блогу
Share:

Похожие статьи