Функциональное программирование: раскрытие возможностей этого набора инструментов.
После того, как мы увидели, что означает функциональное программирование и как оно относится к функциям, как ООП относится к объектам (назначение, передача в качестве параметра и возврат из другой функции), мы углубимся в его практическое применение в этом посте и предстоящие. Не стесняйтесь просматривать предыдущие сообщения здесь:
Чистые функции
Прежде чем мы углубимся в преимущества FP, я хочу поговорить о важном термине: Чистая функция, чистая функция - это функция, реализующая эти две концепции:
- Один и тот же ввод всегда будет давать один и тот же вывод:
пример нарушения этого правила: функция, которая принимает целое число и возвращает его с суммой случайных чисел от 1 до 25, это означает передачу числа 100 для этой функции каждый раз будет давать разные результаты. - Не вызывает побочных эффектов:
пример нарушения этого правила: функция проверяет учетные данные для входа и возвращает, действительны они или нет, но в качестве побочного эффекта генерирует токен и сохраняет это в базе данных, эта функция больше не является чистой, и вызов ее много раз для одного и того же ввода внесет изменения в базу данных.
Чистые функции реагируют только на ввод и производят тот же вывод, и это ничего не меняет снаружи. Простой пример этого:
sum(int first,int second) { first+second }
вызов этой функции миллион раз с входными данными 1, 1 даст 2.
Чтобы узнать больше о преимуществах чистых функций, вы можете просмотреть главу о функциях Чистый код книги дяди Боба.
Абстракция позволяет языку быть более эффективным
Разработчики языков, поддерживающих функциональную парадигму, обычно предлагают множество общих функций высшего порядка, таких как map, foreach, filter и fold. благодаря языку разработчики могут видеть низкоуровневый код языка, они могут оптимизировать использование этих и других функций, как они это сделали с оптимизацией механизма сборщика мусора.
Например, если я хочу распараллелить сопоставление процесс в нефункциональном программировании. Я должен вдаваться в подробности многопоточности и ее головной боли, но для функциональных языков программирования мы уступаем эту ответственность разработчикам языков, большинство функциональных языков программирования предоставляют способ легкого параллельного запуска функционального кода.< br /> Например: Java начал внедрять FP в Java 8, и он предоставил parallelStream, который сообщает компилятору, что я хочу выполнять эти операции параллельно, давайте посмотрим на этот пример:
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// Using parallelStream to parallelize mapping
numbers.parallelStream()
.map(number -> number * 2)
.forEach(number -> System.out.println(number));
}
В этом примере мы запускаем сопоставление одновременно, и метод foreach выводит результаты в непредсказуемом порядке в соответствии с параллелизмом.
Просто имейте в виду, что использование этой функции с нечистыми функциями действительно не рекомендуется и приведет к непредсказуемым проблемам.
Классифицируйте проблемы по-разному, видя общие черты
Если вы заметили map, filter, reduce, find и многие другие операции, они являются решениями для общих проблем, абстрагирование общих проблем — это очень известный подход в FP.
Представьте, что у вас есть проблема A и проблема B. , в обеих есть фильтрация списка данных по определенному условию, в ООП вы можете реализовывать каждую задачу самостоятельно и решать ее, но в ФП вы можете заметить, что в обеих задачах есть операция «фильтрации», но условие другое, так что я могу абстрагироваться от общности фильтрации и сделать условие параметром, и использовать его в обеих задачах.
И когда вы погружаетесь глубже в ФП и используете его все больше и больше, способ, которым вы думаете о решении проблем, будет другим. .
Закрытия
Закрытие — это термин, который происходит от закрытия. Замыкание — это функция, которая несет неявную привязку ко всем переменным, на которые есть ссылки в ней. Другими словами, функция (или метод) заключает в себе контекст вокруг вещей, на которые она ссылается.
Давайте посмотрим, что это значит и чем это полезно:
function createSalaryPredicate(minSalary) {
return function(employee) {
return employee.salary >= minSalary;
}
}
const isHighPaid = createSalaryPredicate(100000);
const isWellPaid = createSalaryPredicate(70000);
const employees = [
{ name: 'John', salary: 90000 },
{ name: 'Alice', salary: 110000 },
{ name: 'Bob', salary: 80000 }
];
const highPaidEmployees = employees.filter(isHighPaid);
const wellPaidEmployees = employees.filter(isWellPaid);
console.log(highPaidEmployees);
console.log(wellPaidEmployees);
Котлин:
data class Employee(val name: String, val salary: Int)
fun createSalaryPredicate(minSalary: Int): (Employee) -> Boolean {
return { employee -> employee.salary >= minSalary }
}
val isHighPaid = createSalaryPredicate(100000)
val isWellPaid = createSalaryPredicate(70000)
val employees = listOf(
Employee("John", 90000),
Employee("Alice", 110000),
Employee("Bob", 80000)
)
val highPaidEmployees = employees.filter(isHighPaid)
val wellPaidEmployees = employees.filter(isWellPaid)
println(highPaidEmployees)
println(wellPaidEmployees)
Мы создали динамический внутренний контекст, который принимает minSalary в качестве параметра и сохраняет этот контекст для последующего использования.
Кроме того, вы можете сохранять переменные в созданном контексте и изменять их, вызывая функцию, см. следующий пример:
function createIncrementer() {
let count = 0;
function increment() {
count++;
console.log(count);
}
return increment;
}
const incrementer1 = createIncrementer();
const incrementer2 = createIncrementer();
incrementer1(); // Output: 1
incrementer1(); // Output: 2
incrementer1(); // Output: 3
incrementer2(); // Output: 1
incrementer2(); // Output: 2
incrementer2(); // Output: 3
fun createIncrementer(): () -> Unit {
var count = 0
return {
count++
println(count)
}
}
val incrementer1 = createIncrementer()
val incrementer2 = createIncrementer()
incrementer1() // Output: 1
incrementer1() // Output: 2
incrementer1() // Output: 3
incrementer2() // Output: 1
incrementer2() // Output: 2
incrementer2() // Output: 3
В императивных языках используется состояниедля моделирования программирования, например передача параметров. Замыкания позволяют нам моделировать поведение путем инкапсуляции как кода, так и контекстав единую конструкцию, замыкание, которое может передаваться как традиционные структуры данных и выполняться точно в нужное время и в нужном месте.
Захватывайте контекст, а не состояние.
Честно говоря, в моей разработке для Android замыкания используются очень редко, но я уверен, что есть случаи, когда замыкание — идеальный способ справиться с ними.
Я думаю, что для этого поста достаточно, в следующем посте мы углубимся в преимущества, которые делают код лучше и повышают читаемость, спасибо, что прочитали все это, я надеюсь, что это что-то изменило в ваших знаниях, удачного кодирования 😍🧑 🏻💻.