Статьи

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

Существует несколько стилей / парадигм программирования, но есть два общеизвестных: императивный и функциональный .

Императивное программирование — самая доминирующая парадигма, поскольку почти все основные языки (C ++, Java, C #) продвигают это. Но в последние несколько лет функциональное программирование стало привлекать внимание. Одним из основных движущих факторов является то, что просто все новые компьютеры поставляются с 4, 8, 16 или более ядрами, и очень сложно написать параллельную программу в императивном стиле для использования всех ядер. Функциональный стиль переносит эту сложность на уровень среды выполнения и освобождает разработчиков от тяжелой и подверженной ошибкам работы.

Подождите! Так в чем же разница между этими двумя стилями.

Императивное программирование — это парадигма, в которой вы говорите, как именно и какие точные операторы машина / среда выполнения должны выполнять для достижения желаемого результата.

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

Функциональный стиль перемещает часть how на уровень времени выполнения и помогает разработчикам сосредоточиться на части what . Абстрагируясь от того, как часть, мы можем написать более поддерживаемое и масштабируемое программное обеспечение.

Чтобы справиться с проблемами, возникающими на многоядерных машинах, и оставаться привлекательными для разработчиков, в Java 8 была введена функциональная парадигма рядом с императивной.

Достаточно теории, давайте реализуем несколько задач программирования в императивном и функциональном стиле с использованием Java и увидим разницу.

Императив последовательности Фибоначчи против функциональности (Последовательность Фибоначчи — это последовательность чисел: 1, 1, 2, 3, 5, 8, 13, 21, 34,… Следующее число можно найти, сложив два числа перед ним.)

Последовательность Фибоначчи в итеративном и императивном стиле

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
public static int fibonacci(int number) {
  int fib1 = 1;
  int fib2 = 1;
  int fibonacci = fib1;
  for (int i = 2; i < number; i++) {
    fibonacci = fib1 + fib2;
    fib1 = fib2;
    fib2 = fibonacci;
  }
  return fibonacci;
}
 
for(int i = 1; i  <= 10; i++) {
  System.out.print(fibonacci(i) +" ");
}
// Output: 1 1 2 3 5 8 13 21 34 55

Как вы можете видеть здесь, мы сосредоточены на том, как (итерация, состояние), а не на том, чего мы хотим достичь.

Последовательность Фибоначчи в итеративном и функциональном стиле

1
2
3
4
5
6
7
8
IntStream fibonacciStream = Stream.iterate(
    new int[]{1, 1},
    fib -> new int[] {fib[1], fib[0] + fib[1]}
  ).mapToInt(fib -> fib[0]);
 
fibonacciStream.limit(10).forEach(fib -> 
    System.out.print(fib + " "));
// Output: 1 1 2 3 5 8 13 21 34 55

Напротив, вы можете видеть здесь, что мы сосредоточены на том, чего мы хотим достичь.

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

Простое число в императивном стиле

1
2
3
4
5
6
7
public boolean isPrime(long number) { 
  for(long i = 2; i <= Math.sqrt(number); i++) { 
    if(number % i == 0) return false
  
  return number > 1
}
isPrime(9220000000000000039L) // Output: true

Опять же, здесь мы сосредоточены на том, как (итерация, состояние).

Prime Number в функциональном стиле

1
2
3
4
5
6
7
public boolean isPrime(long number) { 
  return number > 1 && 
    LongStream
     .rangeClosed(2, (long) Math.sqrt(number)) 
     .noneMatch(index -> number % index == 0);
}
isPrime(9220000000000000039L) // Output: true

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

Теперь вы можете подумать, хм, это все, что мы можем иметь … ? Давайте посмотрим, как мы можем использовать все наши ядра (усиление параллелизма) в функциональном стиле.

1
2
3
4
5
6
7
8
public boolean isPrime(long number) { 
  return number > 1 && 
    LongStream
    .rangeClosed(2, (long) Math.sqrt(number))
    .parallel() 
    .noneMatch(index -> number % index == 0);
}
isPrime(9220000000000000039L) // Output: true

Это оно! Мы только что добавили .parallel () в поток. Вы можете увидеть, как библиотека / среда выполнения обрабатывает сложность для нас.

Факториал Императив против Функционала (Факториал n является произведением всех натуральных чисел, меньших или равных n.)

Факториал в итеративном и императивном стиле

1
2
3
4
5
6
7
8
public long factorial(int n) {
  long product = 1;
  for ( int i = 1; i <= n; i++ ) {
    product *= i;
  }
  return product;
}
factorial(5) // Output: 120

Факториал в итеративном и функциональном стиле

1
2
3
4
5
6
7
public long factorial(int n) {
 return LongStream
   .rangeClosed(1, n)
   .reduce((a, b) -> a *   b)
   .getAsLong();
}
factorial(5) // Output: 120

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

Чтобы увидеть все функциональные возможности, представленные в Java 8, ознакомьтесь со следующими инструкциями по лямбда-выражениям, методам и потокам .