Функциональное программирование является интересной парадигмой, которая поможет по-новому взглянуть на процесс разработки и, несомненно, расширит ваш опыт новыми идеями и подходами.
- Парадигмы программирования
- В чем суть функционального программирования?
- Примеры работы в функциональном стиле
Парадигмы программирования
Для начала обратимся к википедии за определением понятия “парадигма программирования”. Парадигма программирования — это совокупность идей и понятий, определяющих стиль написания компьютерных программ (подход к программированию). Это способ концептуализации, определяющий организацию вычислений и структурирование работы, выполняемой компьютером.
Выделяют две крупные парадигмы программирования: императивная и декларативная.
Императивное программирование предполагает ответ на вопрос “Как?”. В рамках этой парадигмы вы задаете последовательность действий, которые нужно выполнить, для того чтобы получить результат. Результат выполнения сохраняется в ячейках памяти, к которым можно обратиться в последствии.
Декларативное программирование предполагает ответ на вопрос “Что?”. Здесь вы описываете задачу, даете спецификацию, говорите, что вы хотите получить в результате выполнения программы, но не определяете, как этот ответ будет получен.
Каждая из этих парадигм включает в себя более специфические модели. В промышленности наибольшее распространение получили структурное и объектно-ориентированное программирование из группы “императивное программирование” и функциональное программирование из группы “декларативное программирование”.
В рамках структурного подхода к программированию основное внимание сосредоточено на декомпозиции – разбиении программы/задачи на отдельные блоки / подзадачи. Разработка ведётся пошагово, методом “сверху вниз”. Наиболее распространенным языком, который предполагает использование структурного подхода к программирования является язык C, в нем, основными строительными блоками являются функции.
В рамках объектно-ориентированного (ООП) подхода программа представляется в виде совокупности объектов, каждый из которых является экземпляром определенного класса, классы образуют иерархию наследования. ООП базируется на следующих принципах: инкапсуляция, наследование, полиморфизм, абстракция. Примерами языков, которые позволяют вести разработку в этой парадигме являются C#, Java.
В рамках функционального программирования выполнение программы – это процесс вычисления, который трактуется как вычисление значений функций в математическом понимании последних (в отличие от функций как подпрограмм в процедурном программировании). Языки, которые реализуют эту парадигму – это Haskell, Lisp.
Довольно часто бывает так, что дизайн языка позволяет использовать все перечисленные парадигмы (например Python) или только часть из них (например, C++).
В чем суть функционального программирования?
Языки, которые можно отнести в функциональной парадигме обладают определенным набором свойств. Если язык не является чисто функциональным, но реализует эти свойства, то на нем можно разрабатывать, как говорят, в функциональном стиле. Свойства функционального стиля программирования:
- Функции являются объектами первого класса (First Class Object). Это означает, что с функциями вы можете работать, также как и с данными: передавать их в качестве аргументов другим функциям, присваивать переменным и т.п.
- В функциональных языках не используются переменные (как именованные ячейки памяти), т.к. там нет состояний, а т.к. нет переменных, то и нет операции присваивания, как это понимается в императивном программировании.
- Рекурсия является основным подходом для управления вычислениями, а не циклы и условные операторы.
- Используются функции высшего порядка (High Order Functions). Функции высшего порядка – это функций, которые могут в качестве аргументов принимать другие функции.
- Функции являются “чистыми” (Pure Functions) – т.е. не имеют побочных эффектов (иногда говорят: не имеют сайд-эффектов).
- Акцент на том, что должно быть вычислено, а не на том, как вычислять.
- Фокус на работе с контейнерами (хотя это спорное положение).
Python не является функциональным языком программирования, но его возможностей хватает, чтобы разрабатывать программы в функциональном стиле.
Примеры работы в функциональном стиле
Функции, как объекты первого класса
Ниже представлен код, который демонстрирует возможность Python, в части того, что функцию можно присвоить переменной и использовать ее:
>>> def mul5(value): return value*5 >>> v1 = 3 >>> f1 = mul5 >>> f1(v1) 15
Переменной f1, в качестве значения, присваивается функция mul5, после этого появляется возможность вызвать ее как функцию.
Рекурсия
Рекурсия – это вызов функции из нее же самой. Для начала приведем пример не рекурсивной функции, которая считает факториал:
def fact_iter(n): if n == 0 or n == 1: return 1 else: prod = 1 for i in range(1, n + 1): prod *= i return prod print(fact_iter(5))
В качестве результата получим число 120.
Перепишем ее через рекурсию:
def fact_rec(n): if n == 0 or n == 1: return 1 else: return n * fact_rec(n - 1) print(fact_rec(5))
Результат получим аналогичный первому. Заметим, что такая реализация не является эффективной по памяти, о том почему это так, и как сделать правильно см “О рекурсии и итерации“
Функции высшего порядка
Функции высшего порядка принимают в качестве аргументов другие функции. В стандартную библиотеку Python входит достаточно много таких функции, в качестве примера приведем функцию map. Она принимает функцию и список, применяет функцию к каждому элементу списка и возвращает новый модифицированный список.
>>> fn = lambda x: x**2 >>> print(list(map(fn, [1, 2, 3, 4, 5]))) [1, 4, 9, 16, 25]
Чистые функции
Чистые функции – это функции, которые не имеют побочных эффектов. В Python это не выполняется. Необходимо самостоятельно следить за тем, чтобы функция была чистой.
Например, следующая функция модифицирует данные, которые в нее передаются:
def fun_not_clear(data): if len(data) > 0: data[0] += 10 return data*2 d1 = [1, 2, 3] d2 = fun_not_clear(d1) >>> print(d1) [11, 2, 3] >>> print(d2) [11, 2, 3, 11, 2, 3]
В функцию fun_not_clear был передан массив данных, над которым был проведен ряд модификаций и сформирован новый массив. При этом исходный массив тоже был изменен. Это пример побочного эффекта, который можно получить в Python. Более корректный вариант будет выглядеть так:
def fun_clear(data): data = data[:] if len(data) > 0: data[0] += 10 return data*2 d1 = [1, 2, 3] d2 = fun_clear(d1) >>> print(d1) [1, 2, 3] >>> print(d2) [11, 2, 3, 11, 2, 3]
В следующих статьях будут более подробно описаны аспекты функционального программирования, которые можно использовать в Python. Также большое внимание уделим библиотекам, которые предоставляют инструменты, позволяющие более комфортно работать в этой парадигме.
P.S.
Вводные уроки по “Линейной алгебре на Python” вы можете найти соответствующей странице нашего сайта. Все уроки по этой теме собраны в книге “Линейная алгебра на Python”.
Если вам интересна тема анализа данных, то мы рекомендуем ознакомиться с библиотекой Pandas. Для начала вы можете познакомиться с вводными уроками. Все уроки по библиотеке Pandas собраны в книге “Pandas. Работа с данными”.