Парадигмы программирования. Эпизод III: Функциональное программирование
Больше вкусностей найдешь на моем канале - https://t.me/emotional_robotЧтобы примерно (хотя бы, потому что сразу в эту дичь вникнуть не представляется возможным) понять, что такое функциональное программирование, и с чем его едят (и давятся вечно), надо понять, что такое функция. Иначе мозг отключится еще на этапе определения.
Функция в императивном подходе
В данной статье я постарался на конкретных примерах объяснить, чем отличается императивный подход в программировании от декларативного. Затем в статье о процедурном программировании рассказал об особом виде подпрограмм, помогающих лучше организовать код - процедуры. Процедура принимает некоторые параметры (аргументы), которые помогают ей в различных вычислениях и операциях (ввода/вывода, вызова других процедур и так далее).
Однако, во многих ЯП, поддерживающих императивный подход, существует еще один вид подпрограмм, которые называются функциями. Их главное отличие от процедур - они могут возвращать результат своего выполнения.
Спокойно, щас на примере покажу, че почем. Все же знают операцию сложения двух чисел? Надо взять первое число, прибавить к нему второе - получится третье число:
1 + 2 = 3
Для реализации операции суммы двух чисел на языке программирования JavaScript (ну на чем еще, я ж фронтендер) мы напишем такую функцию:
function sum(a, b) {
return a + b;
}
То есть, отличие от процедур только в ключевом слове "function" (в других языках это могут быть слова "func", "fun") и в слове "return", после которого должен быть возвращен результат выполнения функции. В данном случае, вернется результат сложения аргументов "a" и "b". Но куда этот результат вернется?
Да куда угодно, в большинстве случаев, в какую-нибудь переменную:
let result = sum(1, 2);
Мы объявили переменную "result" с помощью ключевого слова "let", и поместили в нее число "3" - результат вычисления функции sum(1, 2).
Таким образом, используя функции и императивный подход, мы можем последовательно вызывать разные функции, передавая разные аргументы, получая разные результаты, что позволит эффективно манипулировать состоянием нашей программы. При этом, ничто не мешает нам прямо в функциях организовывать какие-то побочные эффекты (например, запись промежуточного результата выполнения функции в файл), хотя и не рекомендуется так делать.
Функция в декларативном подходе
Если использовать функцию только как процедуру с возвращением результата, тогда теряется вся мощь, на которую она способна. Поэтому существует декларативный подход и функциональное программирование.
Вот сейчас внимание. Я как не пытался подступиться к этой теме, но не могу с легкостью и юмором объяснить это, поэтому дальше реально пойдет жесть. Если вы морально к этому не готовы, рекомендую остановиться и отдохнуть. Вообще, для новичков достаточно понимания процедурного и объектно-ориентированного программирования, функциональным обычно начинают интересоваться позже, но для полноты картины я все равно решил о нем поведать. Если что, переходите сразу к разделу "Итого".
Функциональное программирование — парадигма программирования, в которой процесс вычисления трактуется как вычисление значений функций в математическом понимании последних.
В математике функция есть соответствие между элементами двух множеств, установленное по такому правилу, что каждому элементу одного множества ставится в соответствие некоторый элемент из другого множества. Простой пример из жизни: каждому человеку можно однозначно поставить в соответствие его биологическую мать.
Улавливаете суть? Одни и те же элементы первого множества по определенному правилу можно однозначно соотнести с элементами другого множества. Никак иначе. Нельзя вызвать функцию два раза с одинаковыми аргументами и получить два разных результата. Это основа функционального программирования.
Функция как раз является тем самым правилом, по которому одни элементы преобразуются в другие:
y = f(x)
Например, y = x * x. Это функция возведения в квадрат некоторой переменной x, результатом которого станет некая переменная y. И во всем функциональном программировании так - вы пишете кучу функций, которые преобразуют одно в другое и передают дальше. У программы нет общего состояния, которое существует при императивном подходе. Поэтому не имеет значения, в какой последовательности вызывать функции. Их даже можно вызывать параллельно. Узнаете декларативный подход? Вы пишете, что нужно делать, а не как.
Кстати, функции, которые возвращают один и тот же результат при одинаковых аргументах и не имеющие побочных эффектов называются чистыми.
И шо нам это дает?
Да до жопы всего. Можно создавать функции высшего порядка, которые могут принимать в качестве аргументов другие функции и возвращать в результате еще одну функцию. Можно мемоизировать (запоминать) результат выполнения функции ради повышения производительности (ну сами рассудите, зачем вычислять каждый раз результат функции при одних и тех же аргументах, если функция уже чистая). Можно функцию вызывать рекурсивно (то есть, функция может вызвать саму себя).
В зависимости от языка программирования (а функциональных языков тоже до жопы, как и языков, поддерживающих ООП), могут добавляться какие-то фишки и особенности: карринг (частичное применение), замыкание, монады, лямбда-абстракции, ленивые вычисления, функторы. Страшно? Честно признаюсь, мне тоже страшно, хотя некоторые из этих вещей использую на работе.
Итого
Есть языки, поддерживающие объектно-ориентированный стиль, и языки, поддерживающие функциональный стиль. До кучи есть мультипарадигмальные языки программирования, на которых можно писать и в объектно-ориентированном, и в функциональном стиле (например, JavaScript). И весь этот зоопарк ЯПов новичка может свести с ума. Как выбрать, на чем писать? По каким параметрам выбирать язык? Что легче? Что интереснее? За знание какого языка больше платят? Кто здесь? Где мой конь?
Я бы при всем желании не смог наставить на верный путь, если не объяснить заранее все концепции и подходы в программировании. Да, можете себе представить, что вся эта вакханалия была лишь подготовкой к нормальным советам? Так и живем.
Правда, прежде, чем я перейду к конкретным советам, я хочу рассказать об еще одном важном делении ЯПов.
Но не волнуйтесь, это деление гораздо проще для понимания, чем парадигмы программирования. Но не менее интересное.