Python/Функциональное программирование на Python
Функциональное программирование является одной из парадигм, поддерживаемых языком программирования Python. Основными предпосылками для полноценного функционального программирования в Python являются: функции высших порядков, развитые средства обработки списков, рекурсия, возможность организации ленивых вычислений. Элементы функционального программирования в Python могут быть полезны любому программисту, так как позволяют гармонично сочетать выразительную мощность этого подхода с другими подходами.
- 1 Возможности
- 1.1 Определение и использование функции
- 1.2 Списковые включения
- 1.3 Встроенные функции высших порядков
- 1.3.1 map()
- 1.3.2 filter()
- 1.3.3 reduce()
- 1.3.4 apply()
Возможности править
Определение и использование функции править
Функция в Python может быть определена с помощью оператора def или лямбда-выражением. Следующие операторы эквивалентны:
def func(x, y): return x**2 + y**2 func = lambda x, y: x**2 + y**2
В определении функции фигурируют формальные аргументы. Некоторые из них могут иметь значения по умолчанию. Все аргументы со значениями по умолчанию следуют после аргументов без значений по умолчанию.
При вызове функции задаются фактические аргументы. Например:
func(2, y=7)
В начале идут позиционные аргументы. Они сопоставляются с именами формальных аргументов по порядку. Затем следуют именованные аргументы. Они сопоставляются по именам и могут быть заданы в вызове функции в любом порядке. Разумеется, все аргументы, для которых в описании функции не указаны значения по умолчанию, должны присутствовать в вызове функции. Повторы в именах аргументов недопустимы.
Функция всегда возвращает только одно значение (или None , если значение не задано в операторе return или этот оператор не встречен по достижении конца определения функции). Однако, это незначительное ограничение, так как возвращаемым значением может быть кортеж.
Определив функцию с помощью лямбда-выражения, можно тут же её использовать:
>>> (lambda x: x+2)(5) 7
Лямбда-выражения удобны для определения не очень сложных функций, которые передаются затем другим функциям.
Функции в Python являются объектами первого класса, то есть, они могут употребляться в программе наравне с объектами других типов данных.
Списковые включения править
w:Списковое включение [1] (англ. list comprehension ) — наиболее выразительное из функциональных средств Python. Например, для вычисления списка квадратов положительных целых чисел, меньших 10, можно использовать выражение:
l = [x**2 for x in range(1,10)]
Встроенные функции высших порядков править
В Python есть функции, одним из аргументов которых являются другие функции: map() , filter() , reduce() , apply() .
map() править
Функция map() позволяет обрабатывать одну или несколько последовательностей с помощью заданной функции:
>>> list1 = [7, 2, 3, 10, 12] >>> list2 = [-1, 1, -5, 4, 6] >>> map(lambda x, y: x*y, list1, list2) [-7, 2, -15, 40, 72]
Аналогичного (только при одинаковой длине списков) результата можно добиться с помощью списочных выражений:
>>> [x*y for x, y in zip(list1, list2)] [-7, 2, -15, 40, 72]
filter() править
Функция filter() позволяет фильтровать значения последовательности. В результирующем списке только те значения, для которых значение функции для элемента истинно:
>>> numbers = [10, 4, 2, -1, 6] >>> filter(lambda x: x 5, numbers) # В результат попадают только те элементы x, для которых x < 5 истинно[4, 2, -1]
То же самое с помощью списковых выражений:
>>> numbers = [10, 4, 2, -1, 6] >>> [x for x in numbers if x 5] [4, 2, -1]
reduce() править
Для организации цепочечных вычислений в списке можно использовать функцию reduce() . Например, произведение элементов списка может быть вычислено так (Python 2):
>>>from functools import reduce >>>numbers = [2, 3, 4, 5, 6] >>> reduce(lambda res, x: res*x, numbers, 1) 720
Вычисления происходят в следующем порядке:
(((2*3)*4)*5)*6
Цепочка вызовов связывается с помощью промежуточного результата ( res ). Если список пустой, просто используется третий параметр (в случае произведения нуля множителей это 1):
>>> reduce(lambda res, x: res*x, [], 1) 1
Разумеется, промежуточный результат необязательно число. Это может быть любой другой тип данных, в том числе и список. Следующий пример показывает реверс списка:
>>> reduce(lambda res, x: [x]+res, [1, 2, 3, 4], []) [4, 3, 2, 1]
Для наиболее распространенных операций в Python есть встроенные функции:
>>> numbers = [1, 2, 3, 4, 5] >>> sum(numbers) 15 >>> list(reversed(numbers)) [5, 4, 3, 2, 1]
В Python 3 встроенной функции reduce() нет, но её можно найти в модуле functools .
apply() править
Функция для применения другой функции к позиционным и именованным аргументам, заданным списком и словарем соответственно (Python 2):
>>> def f(x, y, z, a=None, b=None): . print x, y, z, a, b . >>> apply(f, [1, 2, 3], 'a': 4, 'b': 5>) 1 2 3 4 5
В Python 3 вместо функции apply() следует использовать специальный синтаксис:
>>> def f(x, y, z, a=None, b=None): . print(x, y, z, a, b) . >>> f(*[1, 2, 3], **'a': 4, 'b': 5>) 1 2 3 4 5
Замыкания править
Функции, определяемые внутри других функций, представляют собой полноценные замыкания (англ. closures ) [2] :
def multiplier(n): "multiplier(n) возвращает функцию, умножающую на n" def mul(k): return n*k return mul # того же эффекта можно добиться выражением # multiplier = lambda n: lambda k: n*k mul2 = multiplier(2) # mul2 - функция, умножающая на 2, например, mul2(5) == 10
Итераторы править
Другие средства функционального программирования доступны из стандартной библиотеки (например, модуль itertools ) и других библиотек.
Следующий пример иллюстрирует применение перечисляющего и сортирующего итераторов (итератор не может быть напечатан оператором print , поэтому оставшиеся в нем значения были помещены в список):
>>> it = enumerate(sorted("PYTHON")) # итератор для перечисленных отсортированных букв слова >>> it.next() # следующее значение (0, 'H') >>> print list(it) # оставшиеся значения в виде списка [(1, 'N'), (2, 'O'), (3, 'P'), (4, 'T'), (5, 'Y')]
Следующий пример иллюстрирует использование модуля itertools :
>>> from itertools import chain >>> print list(chain(iter("ABC"), iter("DEF"))) ['A', 'B', 'C', 'D', 'E', 'F']
В следующем примере иллюстрируется функция groupby (группировать по), с помощью которой порождается список пар значение ключа и соответствующий ключу итератор (в этот итератор собраны все значения исходного списка с одинаковым значением ключа). В примере ключом является True или False в зависимости от положительности значения. (Для целей вывода каждый итератор превращается в список).
from math import cos from itertools import groupby lst = [cos(x*.4) for x in range(30)] # косинусоида [list(y) for k, y in groupby(lst, lambda x: x > 0)] # группы положительных и отрицательных чисел
В модуле itertools есть и другие функции для работы с итераторами, позволяющие кратко (в функциональном стиле) и с вычислительной точки зрения — эффективно — выразить требуемые процессы обработки списков.
Модуль functools править
В Python 2.5 появился модуль functools и в частности возможность частичного применения функций:
>>> from functools import partial >>> def myfun(a, b): return a + b . >>> myfun1 = partial(myfun, 1) >>> print myfun1(2) 3
(Частичное применение функций также можно реализовать с помощью замыканий или функторов)
Ленивые вычисления править
Ленивые вычисления можно организовать в Python несколькими способами, используя различные механизмы:
- простейшие логические операции or и and не вычисляют второй операнд, если результат определяется первым операндом
- лямбда-выражения
- определенные пользователем классы с ленивой логикой вычислений [3] или функторы
- Генераторы и генераторные выражения
- (Python 2.5) if-выражение имеет «ленивую» семантику (вычисляется только тот операнд, который нужен)
Пример, который иллюстрирует работу if-выражения. С помощью оператора print можно проследить, какие функции реально вызывались:
>>> def f(): . print "f" . return "f" . >>> def g(): . print "g" . return "g" . >>> f() if True else g() f 'f' >>> f() if False else g() g 'g'
Некоторые примеры из книги рецептов:
- Ленивая сортировка [4]
- Ленивый обход графа [5]
- Ленивое вычисление свойств [6]
- Карринг [7]
Функторы править
Функторами называют объекты, синтаксически подобные функциям, то есть поддерживающие операцию вызова. Для определения функтора нужно перегрузить оператор () с помощью метода __call__ . В Python функторы полностью аналогичны функциям, за исключением специальных атрибутов ( func_code и некоторых других). Например, функторы можно передавать в качестве функций обратного вызова (callback) в С-код. Функторы позволяют заменить некоторые приёмы, связанные с использованием замыкания, статических переменных и т. п.
Ниже представлено замыкание и эквивалентный ему функтор:
def addClosure(val1): def closure(val2): return val1 + val2 return closure class AddFunctor(object): def __init__(self, val1): self.val1 = val1 def __call__(self, val2): return self.val1 + val2 cl = addClosure(2) fn = AddFunctor(2) print cl(1), fn(1) # напечатает "3 3"
Следует отметить, что код, использующий замыкание, будет исполняться быстрее, чем код с функтором. Это связанно с необходимостью получения атрибута val у переменной self (то есть функтор проделывает на одну Python операцию больше). Также функторы нельзя использовать для создания декораторов с параметрами. С другой стороны, функторам доступны все возможности ООП в Python, что делает их очень полезными для функционального программирования. Например, можно написать функтор, который будет «запоминать» исполняемые над ним операции и затем повторять их. Для этого достаточно соответствующим образом перегрузить специальные методы.
class SlowFunctor(object): def __init__(self,func): self.func = func def __add__(self,val): # сложение функтора с чем-то if isinstance(val,SlowFunctor): # если это функтор new_func = lambda *dt,**mp : self(*dt,**mp) + val(*dt,**mp) else: # если что-то другое new_func = lambda *dt,**mp : self(*dt,**mp) + val return SlowFunctor( new_func ) def __call__(self,*dt): return self.func(*dt) import math def test1(x): return x + 1 def test2(x): return math.sin(x) func = SlowFunctor(test1) # создаем функтор func = func + SlowFunctor(test2) # этот функтор можно складывать с функторами func = (lambda x : x + 2)(func) # и числами, передавать в качестве параметра в функции # как будто это число def func2(x): # Эквивалентная функция return test1(x) + test2(x) + 2 print func(math.pi) # печатает 3.14159265359 print func(math.pi) - func2(math.pi) # печатает 0.0
Функторы привносят в Python возможность ленивых вычислений, присущую функциональным языкам: вместо вычисления результата выражения — динамическое определение новых функций комбинированием имеющихся.
Определенный подобным образом функтор создает значительные накладные расходы, так как при каждом вызове проходит по вызовам всех вложенных lambda . Можно оптимизировать функтор, применив технику генерирования байткода во время исполнения. Соответствующий пример и тесты на скорость есть в Примерах Python программ. При использовании этой техники скорость исполнения не будет отличаться от «статического» кода (если не считать времени, требующегося на однократное конструирование результирующей функции). Вместо байтокода Python можно генерировать на выходе, например, код на языке программирования C, других языках программирования или XML-файлы.
Несмотря на накладные расходы, ленивое вычисление может дать заметный выигрыш в скорости в случаях, когда действия, оборачиваемые ленивым функтором, достаточно дороги — например, включают объёмные вычисления или доступ к диску. Предположим некоторый промежуточный результат X лениво вычисляется перед условным оператором; для него будет создана цепочка функторов. В той ветке условного оператора, где значение X не требуется по ходу вычисления, эта цепочка функторов будет просто отброшена, не приведя к дорогостоящему вычислению. В другой ветке, где X требуется для вычисления конечного результата функции, цепочка функторов произведёт его вычисление. При этом программисту не нужно отслеживать, в какой из веток алгоритма значение может не потребоваться: он может рассчитывать, что дорогостоящее вычисление X произойдёт только тогда, когда его результат не будет отброшен.
Примечания править
- ↑Прохоренок, 2011, с. 123-124
- ↑Dive into Python 3, Ch. 6. Closures and Generators
- ↑Пакет lazypy
- ↑Lazy sorting
- ↑Lazy Traversal of Directed Graphs
- ↑Lazy property evaluation
- ↑curry — associating parameters with a function
Ссылки править
- Functional Programming with Python, Pramode C.E., Linux Gazette, 2004
- Charming Python: Functional programming in Python, David Mertz
- Набор инструментов Xoltar для ФП (и не только)
- Functional Programming HOWTO, A. M. Kuchling
- Closures (Re: функциональное программирование на Python)
Литература править
- Прохоренок Н. А. Python. Самое необходимое. — БХВ-Петербург, 2011. — 416 с. — ISBN 978-5-9775-0614-4
- David M. Beazley Python Essential Reference, 4th Edition. — Addison-Wesley Professional, 2010. — 744 с. — ISBN 978-0-672-32978-4 Часть главы 6 на LinuxJournal: Functions and functional programming (англ.)
- Mark Pilgrim. Dive Into Python. — Apress, 2004. — 413 с. — ISBN 9781590593561 . Глава о функциональном программировании (англ.)
- David Mertz Functional Programming in Python. — O’Reilly, 2015. — ISBN 978-1-491-92856-1 (англ.)
- Steven Lott Functional Python Programming. — Packt Publishing Ltd, 2015. — 360 p. — ISBN 978-1-78439-761-6 (англ.)
Язык программирования Python – причины популярности, преимущества и области применения
Из этой статьи вы узнаете о том, чем интересен, полезен и примечателен язык программирования Питон, когда он возник и как развивался, где применяется и почему стал таким популярным. Также мы расскажем о том, какие известные компании и проекты используют Python, какие у него есть недостатки и перспективы, а еще о целесообразности изучения этого языка.
В октябре 2021 года самым популярным языком программирования был признан Python, что стало для него самым весомым достижением за последние несколько десятилетий. Он сумел обойти своих основных конкурентов (знаменитые языки С и Java) в авторитетном рейтинге компании Tiobe. Это исследование ежемесячно определяет самые востребованные среди программистов языки, начиная с 2003 года. Python четырежды получал звание «Язык года» (в 2007, 2010, 2018, 2020), однако почетное первое место в рейтинге ему удалось занять впервые.
Что такое Python? История его возникновения и развития
Python – это высокоуровневый объектно-ориентированный и структурный язык программирования общего назначения, который относится к категории интерпретируемых языков и не требует компиляции. Он представляет собой скриптовый язык и отличается высокой степенью универсальности. Благодаря этому, оптимально подходит для множества платформ и задач, от серверных ОС до мобильных приложений под iOS/Android.
Язык Python был разработан голландским инженером Гвидо ван Россумом в 1991 году. В те времена он работал в Национальном исследовательском институте математики и информатики, где занимался созданием другого языка программирования – ABC. Питон же был его любительским проектом, который позиционировался как понятный и удобный язык, легко изучаемый с нуля. Россум разработал первый рабочий прототип Python на своем домашнем компьютере за несколько выходных и назвал его в честь популярного тогда телешоу «Воздушный цирк Монти Пайтона».
До конца 90-х годов Питон развивался исключительно как хобби его создателя, а в 1999 году Гвидо ванн Россум заключил контракт с компанией BeOpen, которая занялась продвижением технологии и поддержкой ее разработки. В годы сотрудничества с BeOpen было выпущено масштабное обновление языка – Python 2.0. В нем все данные проекта были переведены на SourceForge – крупный хостинг и сообщество для разработчиков открытого ПО, что позволило программистам со всего мира подключиться к работе над совершенствованием этого языка.

Со временем Россум расторг контракт с BeOpen, начав сотрудничать с другой компанией под названием Digital Creations. При взаимодействии с ней команда разработчиков продолжила развивать Python, в частности, появилась версия 2.1, куда была добавлена иерархия функций и новые объекты. В декабре 2008 года вышло очередное масштабное обновление языка – Python 3.0 (Python 3000, Py3k), которое ощутимо усовершенствовало технологию, избавив ее от ряда недостатков архитектуры. Версия 3.0 является актуальной не сегодняшний день, а поддержка Python 2 была прекращена в 2020 году.
Особенности и возможности языка Питон
- Он поддерживает множество парадигм программирования, в том числе объектно-ориентированное, функциональное, императивное, структурное, процедурное, логическое, контрактное, аспектно-ориентированное, метапрограммирование и т.д.
- Программирование на Python позволяет разбивать программы на составные части – модули, которые можно объединять в пакеты.
- Поддерживается полная интроспекция, что позволяет получить информацию о типе и внутренней структуре любого объекта в процессе выполнения программы.
- Обширная стандартная библиотека имеет набор модулей для работы с ОС, различными сетевыми протоколами, архивами, мультимедийными форматами, текстовыми кодировками, регулярными выражениями, криптографическими протоколами и т.д. Поддерживает юнит-тестирование.
- Также возможности Питона включают итераторы и генераторы, обработку исключений, управление контекстом выполнения, декораторы и многое другое.
Преимущества и недостатки Python
Программирование на Python обладает широким спектром достоинств, что и сделало этот язык таким востребованным. К ним относятся:
- Скорость разработки. Для написания программ на Питоне требуется гораздо меньший объем кода, чем в случае с другими популярными языками (Java, C). Это заметно ускоряет разработку, позволяя создавать сложное ПО быстрее, чем на других ЯП.
- Логичный синтаксис. Логичный синтаксис этого языка упрощает чтение и понимание его кода, благодаря чему его довольно легко освоить. Python по праву считается одним из наиболее подходящих языков программирования для начинающих.
- Разнообразие библиотек. Помимо стандартной библиотеки, для Питона доступен большой выбор дополнительных библиотек. Среди самых популярных из них стоит отметить SQLAlchemy (для работы с базами данных), Pygame (для разработки мультимедийных приложений и игр), Flask и Django (для разработки серверной части ПО), NumPy (для разработки в сфере машинного обучения и искусственного интеллекта), Pandas (для обработки big data) и т.д.
- Масштабируемость. Написанные на Питоне программы и приложения легко расширяются и масштабируются, благодаря возможности адаптации их высокоуровневой логики.
- Универсальность. Python – это интерпретируемый язык, который используется для кодинга практически на все современных платформах. Он не нуждается в компиляции и его код можно писать в обычном текстовом документе.
- Глобальное сообщество. Одним из важных факторов бурной популяризации Питона считается многочисленное сообщество разработчиков и энтузиастов данного языка. Его развитие ведется на базе регулярно обновляемой и четко регламентированной документации PEP (предложений по развитию Python).

Вместе с тем, Питон обладает и некоторыми недостатками, среди которых стоит отметить:
- Недостаточное быстродействие. Сам по себе Python не очень подходит для разработки высокопроизводительных проектов – для этого его приходится комбинировать с другими языками. Причиной этому является его устаревший код GIL (Global Interpreter Lock), который не менялся с самой первой версии языка. Поэтому ПО на Питоне работает не так быстро, как на некоторых других языках.
- Динамическая типизация. Из-за нее Python является довольно ресурсоемким языком программирования и потребляет немалые объемы памяти.
Как и где применяется Python?
Универсальность, простота и высокая скорость написания кода на Питоне позволяют использовать его в разных сферах, включая веб-разработку, десктопные программы, системное администрирование, Data Science, встроенные системы, мобильные приложения, компьютерные игры, а также разнообразные плагины и скрипты. Далее мы расскажем о каждом из этих направлений более подробно:
- Веб-разработка. Наиболее востребованная область применения Питона, чаще всего его задействуют для разработки веб-приложений, при помощи созданных на его основе фреймворков: Django, Flask, Pyramid, CherryPy и т.д. Отдельно стоит отметить разработанные на Python движки для сайтов: Django-CMS, Abilian SBE, Saleor и т.д., а еще ПО для парсинга данных в интернете.
- Десктоп-программы. Хотя Питон – это интерпретируемый язык, но он также подходит и для разработки программ для ПК. Среди примеров популярного ПО на Python можно упомянуть визуальный редактор GIMP, центр приложений Ubuntu Software Center, а также известный торрент-клиент BitTorrent (до 6-й версии).
- Встроенные системы для устройств. Разработанное на Питоне ПО используется в мини-компьютерах Raspberry Pi, банкоматах, телеком-оборудовании, станках с ЧПУ и многих других типах устройств.
- Компьютерные игры. Python часто применяют для разработки скриптов сцен, событий и персонажей в компьютерных играх. Например, его задействовали для создания таких популярных игр, как World of Tanks, EVE Online, Battlefield, Civilization и т.д.
- Системное администрирование. В этой сфере Питон помогает автоматизировать выполнение разных задач, а простота и логичность его синтакиса позволяет быстро находить ошибки в коде. Также он является стандартным языком программирования на всех серверах с Linux.
- Машинное обучение и искусственный интеллект. МО и ИИ является одной из самых востребованных и перспективных сфер применения языка. Для этих целей существуют специальные библиотеки Python (TensorFlow, Scikit-Learn), предназначенные для обучения нейросетей, управления промышленными системами и выполнения ряда других задача в области ML/AI.
- Большие данные. Питон активно используется для работы с Big Data, помогая анализировать и визуализировать их, а также прогнозировать тенденции. В этой области также доступен набор специализированных библиотек для вычислений и обработки больших данных: SciPy, Matplotlib, PANDAS.
Стоит ли изучать Питон? Как это лучше сделать?
Python по праву считается одним из самых востребованных языков программирования общего назначения. Его используют в самых разных сферах и на разных уровнях разработки – от некоммерческих проектов и стартапов до международных корпораций. При этом он постоянно развивается, благодаря обширному набору расширений и дополнительных библиотек. В частности, интервал между релизами новых версий Питона в последние годы сократился с 18 до 9 месяцев.

Python весьма популярен среди начинающих программистов в качестве первого языка. Простота и логичность позволяет им освоить азы его синтаксиса и научиться писать простейшие программы буквально в первую неделю обучения. Что касается длительности обучения с нуля до уровня Junior, то на это может уйти от 3 до 12 месяцев.
Парадигмы программирования: примеры парадигм для новичков

Что такое парадигмы программирования? Это не более, чем просто замысловатое название для популярных способов и стилей организации процесса написания программного кода.
Я постараюсь разбить эту тему на части и дать простое пояснение по каждой парадигме. Таким образом, вы сможете легко понять, о чем говорят люди, когда произносят такие слова, как «объектно-ориентированный», «функциональный» или «декларативный». Давайте начнем!
Что такое парадигма программирования?
Парадигмы программирования – это различные способы и стили, которые используются для организации программы или языка программирования. Каждая парадигма состоит из определенных структур, функций и взглядов на то, как следует решать известные задачи программирования.
Вопрос о том, почему существует так много различных парадигм программирования, схож с вопросом о том, почему существует так много языков программирования. Определенные парадигмы лучше подходят для определенных типов задач. Именно поэтому имеет смысл использовать разные парадигмы для разных типов проектов.
Кроме того, методики, которые составляют каждую парадигму, развивались с течением времени. Благодаря достижениям как в области программного, так и аппаратного обеспечения появились различные подходы к решению задач, которых раньше просто не было.
И последняя причина – я думаю, это просто творческое начало в человеке. По своей натуре, нам просто нравится создавать новые вещи, улучшать то, что другие когда-то создали, и адаптировать инструменты под себя и свои предпочтения или просто делать их более эффективными (в нашем понимании).
Все это привело к тому, что на сегодняшний день мы имеем огромное количество вариантов, которые могут помочь нам написать и структурировать ту или иную программу.
Чем парадигма программирования не является?
Парадигмы программирования – это не языки и не инструменты. Вы не сможете ничего «создать» с помощью парадигмы. Они больше похожи на некий набор образцов и руководящих принципов, о которых условились большое количество людей, которым они следовали и которые они подробно изложили.
Язык программирования не всегда привязан к определенной парадигме. Есть языки, которые были созданы с учетом определенной парадигмы и имеют функции, которые облегчают программирование в этом контексте больше, чем другие (хороший пример – Haskel и функциональное программирование).
Однако существуют и «многопарадигмальные» языки. Это означает, что вы можете адаптировать свой код, чтобы он подходил под какую-то из парадигм (хороший пример – JavaScript и Python).
При этом парадигмы программирования не являются взаимоисключающими в том смысле, что вы можете без каких-либо проблем использовать приемы из различных парадигм одновременно.
Популярные парадигмы программирования
Теперь, когда вы знаете, что такое парадигмы программирования, а что к ним не относится, давайте рассмотрим самые популярные из них, их характеристики и сравним их.
Имейте в виду, что этот список не полный. Существуют и другие парадигмы программирования, которые мы здесь рассматривать не будем. Здесь я расскажу вам только о самых популярных и широко используемых.
Императивное программирование
Императивное программирование – это набор подробных инструкций, которые даются компьютеру, чтобы тот выполнил их в заданном порядке. Этот тип программирования называется «императивным», потому что мы некоторым образом указываем компьютеру (как программисты), что он должен делать.
Императивное программирование концентрируется на описании того, как программа работает, шаг за шагом.
Допустим, вы хотите испечь торт. Ваша императивная программа для такого рода задачи может выглядеть следующим образом:
1- Pour flour in a bowl 2- Pour a couple eggs in the same bowl 3- Pour some milk in the same bowl 4- Mix the ingredients 5- Pour the mix in a mold 6- Cook for 35 minutes 7- Let chill
Воспользуемся конкретным примером и предположим, что мы хотим отфильтровать массив чисел так, чтобы остались только числа, которые больше 5. Наш императивный код тогда будет выглядеть следующим образом:
const nums = [1,4,3,6,7,8,9,2] const result = [] for (let i = 0; i < nums.length; i++) < if (nums[i] >5) result.push(nums[i]) > console.log(result) // Output: [ 6, 7, 8, 9 ]
Обратите внимание, что мы указываем программе, что нужно перебрать каждый элемент массива, сравнить каждый из них с 5 и, если элемент больше 5, то поместить его в конечный массив.
Наши инструкции предельно детализированы и конкретны, и именно это и является императивным программированием.
Процедурное программирование
Процедурное программирование – это производное от императивного программирования только с функциями (также известных как «процедуры» или «подпрограммы»).
Процедурное программирования предлагает пользователю разделить выполнение программы на функции, чтобы оптимизировать модульный принцип организации.
Вернемся к нашему примеру с тортом. Процедурная программа для этого примера будет выглядеть следующим образом:
function pourIngredients() < - Pour flour in a bowl - Pour a couple eggs in the same bowl - Pour some milk in the same bowl >function mixAndTransferToMold() < - Mix the ingredients - Pour the mix in a mold >function cookAndLetChill() < - Cook for 35 minutes - Let chill >pourIngredients() mixAndTransferToMold() cookAndLetChill()
Как вы можете видеть, благодаря реализации функций, мы можем просто прочитать три вызова функций в конце файла и понять, что делает наша программа.
Такое упрощение и абстрактное представление является одним из преимуществ процедурного программирования. Однако внутри функций находится все тот же императивный код.
Функциональное программирование
Функциональное программирование продвигает концепцию создания функций немного дальше.
В функциональном программировании функции рассматриваются как «полноправные граждане». Это означает, что их можно присваивать переменным, передавать в качестве аргумента и возвращать в качестве результата других функций.
Еще одна ключевая концепция – это идея чистых функций. Чистая функций – это функция, которая, чтобы получить результат, полагается только на свои входные данные. И при одних и тех же входных данных всегда будет один и тот же результат. Кроме того, эти функции не имеют никаких побочных эффектов (то есть не вносят никаких изменений вне контекста функции).
С учетом всех этих концепций, функциональное программирование призывает писать программы с помощью функций. Оно также поддерживает идею о том, что модульность кода и отсутствие побочных эффектов облегчают определение и разделение обязанностей внутри кодовой базы. Таким образом, это облегчает сопровождение кода.
Вернемся к примеру с фильтрацией массива. В императивной парадигме мы можем использовать внешнюю переменную для хранения результата функции, что по сути может считаться побочным эффектом.
const nums = [1,4,3,6,7,8,9,2] const result = [] // External variable for (let i = 0; i < nums.length; i++) < if (nums[i] >5) result.push(nums[i]) > console.log(result) // Output: [ 6, 7, 8, 9 ]
Для того, чтобы преобразовать это в функциональное программирование, мы можем сделать следующее:
const nums = [1,4,3,6,7,8,9,2] function filterNums() < const result = [] // Internal variable for (let i = 0; i < nums.length; i++) < if (nums[i] >5) result.push(nums[i]) > return result > console.log(filterNums()) // Output: [ 6, 7, 8, 9 ]
Это практически тот же самый код, но мы проворачиваем все итерации внутри функции, в которой мы также сохраняем и массив результатов. Таким образом, мы можем гарантировать, что функция не будет ничего менять за своими пределами. Она создает переменную только для обработки своей собственной информации, и после завершения своей работы удаляет ее.
Декларативное программирование
Декларативное программирование скрывает всю сложность и приближает языки программирования к человеческому языку и мышлению. Это абсолютная противоположность императивному программированию, хотя бы потому что программист дает инструкции не о том, как компьютеру следует решать задачу, а о том, какой требуется результат.
Будет намного понятнее, если мы приведем пример. Воспользуемся примером с фильтрацией массива. Декларативный подход здесь будет выглядеть следующим образом:
const nums = [1,4,3,6,7,8,9,2] console.log(nums.filter(num => num > 5)) // Output: [ 6, 7, 8, 9 ]
Обратите внимание, что, используя функцию фильтрации filter , мы явно не указываем компьютеру перебирать массив или сохранять значения в отдельном массиве. Мы просто говорим о том, что мы хотим («filter») и условие, которое необходимо выполнить («num > 5»).
Что хорошего в таком подходе? Его легче читать и понимать, и зачастую он более емкий в записи. Хорошими примерами декларативного кода являются функции filter , map , reduce и sort в JavaScript.
Еще один хороший пример – современные фреймворки/библиотеки JS, такие как React. Посмотрите, например, на этот код:
Здесь у нас есть кнопка (button) с приемником событий, который запускает функцию console.log при нажатии кнопки.
Синтаксис JSX (то, что использует React) совмещает HTML и JS. Это упрощает и ускоряет написание приложений. Но это не то, что браузеры читают и выполняют. Код React позже преобразуются в обычный HTML и JS, а вот это уже то, с чем работают браузеры.
JSX является декларативным, поскольку его цель заключается в том, чтобы предоставить разработчикам более удобный и эффективный интерфейс для работы.
Здесь также важно отметить, что в декларативном программировании компьютер все равно обрабатывает информацию как императивный код.
Если снова вернуться к примеру с массивом, то компьютер по-прежнему выполняет итерацию по массиву, как в цикле for , но нам, как программистам, не нужно писать это напрямую. Декларативное программирование скрывает всю сложность от программиста.
Объектно-ориентированное программирование
Одной из самых популярных парадигм программирование является объектно-ориентированное программирование (ООП).
Основная концепция ООП заключается в разделении понятий на сущности, которые описываются как некие объекты. Каждая сущность группирует заданный набор информации (свойств) и действий (методов), которые может выполнять эта сущность.
ООП широко использует классы. Классы — это способ создания новых объектов с помощью макета или шаблона, который задает программист. Объекты, которые были созданы с помощью класса, называются экземплярами.
Вернемся к примеру с приготовлением пищи на псевдокоде. Предположим, что в нашей пекарне у нас есть главный повар (по имени Фрэнк) и помощник повара (по имени Энтони). У каждого их них есть определенные обязанности. Если бы мы использовали ООП, то наша программа бы выглядеть следующим образом:
// Create the two classes corresponding to each entity class Cook < constructor constructor (name) < this.name = name >mixAndBake() < - Mix the ingredients - Pour the mix in a mold - Cook for 35 minutes >> class AssistantCook < constructor (name) < this.name = name >pourIngredients() < - Pour flour in a bowl - Pour a couple eggs in the same bowl - Pour some milk in the same bowl >chillTheCake() < - Let chill >> // Instantiate an object from each class const Frank = new Cook('Frank') const Anthony = new AssistantCook('Anthony') // Call the corresponding methods from each instance Anthony.pourIngredients() Frank.mixAndBake() Anthony.chillTheCake()Преимущество ООП заключается в том, что оно облегчает понимание программы за счет четкого разделения задач и обязанностей.
Итоги
Как мы увидели, парадигмы программирования – это различные способы решения задач программирования и организации нашего кода.
Одними из самых популярных и широко используемых на сегодняшний день парадигм являются императивная, процедурная, функциональная, декларативная и объектно-ориентированная. Знание о том, что они из себя представляют, полезно для общего развития, а также для лучшего понимания других тем, связанных с программированием.
«Забытые» парадигмы программирования

Получилось так, что те парадигмы, которые раньше потом и кровью пробивались в свет через орды приверженцев традиционных методов постепенно забываются. Эти парадигмы возникли на заре программирования и то, почему они возникали, какие преимущества они давали и почему используются до сих пор полезно знать любому разработчику.
Ладно. Введение это очень весело, но вы его все равно не читаете, так что кому интересно — добро пожаловать под кат!
Императивное программирование

Исторически сложилось так, что подавляющее большинство вычислительной техники, которую мы программируем имеет состояние и программируется инструкциями, поэтому первые языки программирования в основном были чисто императивными, т.е. не поддерживали никаких парадигм кроме императивной.
Это были машинные коды, языки ассемблера и ранние высокоуровневые языки, вроде Fortran.
Ключевые моменты:
В этой парадигме вычисления описываются в виде инструкций, шаг за шагом изменяющих состояние программы.
В низкоуровневых языках (таких как язык ассемблера) состоянием могут быть память, регистры и флаги, а инструкциями — те команды, что поддерживает целевой процессор.
В более высокоуровневых (таких как Си) состояние — это только память, инструкции могут быть сложнее и вызывать выделение и освобождение памяти в процессе своей работы.
В совсем высокоуровневых (таких как Python, если на нем программировать императивно) состояние ограничивается лишь переменными, а команды могут представлять собой комплексные операции, которые на ассемблере занимали бы сотни строк.
Основные понятия:
Порожденные понятия:
— Присваивание
— Переход
— Память
— УказательЯзыки поддерживающие данную парадигму:
Как основную:
— Языки ассемблера
— Fortran
— Algol
— Cobol
— Pascal
— C
— C++
— AdaКак вспомогательную:
— Python
— Ruby
— Java
— C#
— PHP
— Haskell (через монады)Стоит заметить, что большая часть современных языков в той или иной степени поддерживает императивное программирование. Даже на чистом функциональном языке Haskell можно писать императивно.
Структурное программирование

Структурное программирование — парадигма программирования (также часто встречающееся определение — методология разработки), которая была первым большим шагом в развитии программирования.
Основоположниками структурного программирования были такие знаменитые люди как Э. Дейкстра и Н. Вирт.
Языками-первопроходцами в этой парадигме были Fortran, Algol и B, позже их приемниками стали Pascal и C.
Ключевые моменты:
Эта парадигма вводит новые понятия, объединяющие часто используемые шаблоны написания императивного кода.
В структурном программировании мы по прежнему оперируем состоянием и инструкциями, однако вводится понятие составной инструкции (блока), инструкций ветвления и цикла.
Благодаря этим простым изменениям возможно отказаться от оператора goto в большинстве случаев, что упрощает код.
Иногда goto все-же делает код читабельнее, благодаря чему он до сих пор широко используется, несмотря на все заявления его противников.
Основные понятия:
— Блок
— Цикл
— ВетвлениеЯзыки поддерживающие данную парадигму:
Как основную:
— C
— Pascal
— BasicКак вспомогательную:
— C#
— Java
— Python
— Ruby
— JavaScriptПоддерживают частично:
— Некоторые макроассемблеры (через макросы)Опять-же большая часть современных языков поддерживают структурную парадигму.
Процедурное программирование

Опять-же возрастающая сложность программного обеспечения заставила программистов искать другие способы описывать вычисления.
Собственно еще раз были введены дополнительные понятия, которые позволили по-новому взглянуть на программирование.
Этим понятием на этот раз была процедура.
В результате возникла новая методология написания программ, которая приветствуется и по сей день — исходная задача разбивается на меньшие (с помощью процедур) и это происходит до тех пор, пока решение всех конкретных процедур не окажется тривиальным.
Ключевые моменты:
Процедура — самостоятельный участок кода, который можно выполнить как одну инструкцию.
В современном программировании процедура может иметь несколько точек выхода (return в C-подобных языках), несколько точек входа (с помощью yield в Python или статических локальных переменных в C++), иметь аргументы, возвращать значение как результат своего выполнения, быть перегруженной по количеству или типу параметров и много чего еще.
Основные понятия:
Порожденные понятия:
— Вызов
— Аргументы
— Возврат
— Рекурсия
— ПерегрузкаЯзыки поддерживающие данную парадигму:
Как основную:
— C
— C++
— Pascal
— Object PascalКак вспомогательную:
— C#
— Java
— Ruby
— Python
— JavaScriptПоддерживают частично:
— Ранний BasicСтоит отметить, что несколько точек входа из всех этих языков поддерживаются только в Python.
Модульное программирование

Который раз увеличивающаяся сложность программ заставила разработчиков разделять свой код. На этот раз процедур было недостаточно и в этот раз было введено новое понятие — модуль.
Забегая вперед скажу, что модули тоже оказались неспособны сдержать с невероятной скоростью растущую сложность ПО и в последствии появились пакеты (это тоже модульное программирование), классы (это уже ООП), шаблоны (обобщенное программирование).
Программа описанная в стиле модульного программирования — это набор модулей. Что внутри, классы, императивный код или чистые функции — не важно.
Благодаря модулям впервые в программировании появилась серьезная инкапсуляция — возможно использовать какие-либо сущности внутри модуля, но не показывать их внешнему миру.
Ключевые моменты:
Модуль — это отдельная именованная сущность программы, которая объединяет в себе другие программные единицы, близкие по функциональности.
Например файл List.mod включающий в себя класс List
и функции для работы с ним — модуль.Папка Geometry, содержащая модули Shape, Rectangle и Triangle — тоже модуль, хоть и некоторые языки разделяют понятие модуля и пакета (в таких языках пакет — набор модулей и/или набор других пакетов).
Модули можно импортировать (подключать), для того, чтобы использовать объявленные в них сущности.
Основные понятия:
Порожденные понятия:
Языки поддерживающие данную парадигму:
Как основную:
— Haskell
— Pascal
— PythonКак вспомогательную:
— Java
— C#
— ActionScript 3Поддерживают частично:
— C/C++В некоторых языках для модулей введены отдельные абстракции, в других же для реализации модулей можно использовать заголовочные файлы (в C/C++), пространства имен, статические классы и/или динамически подключаемые библиотеки.
Вместо заключения
В данной статье я не описал популярные сейчас объектно-ориентированное, обобщенное и функциональное программирование. Просто потому, что у меня есть свое, довольно радикальное мнение на этот счет и я не хотел разводить холивар. По крайней мере сейчас. Если тема окажется полезной для сообщества я планирую написать несколько статей, изложив основы каждой из этих парадигм подробно.
Также я ничего не написал про экзотические парадигмы, вроде автоматного, аппликативного, аспект/агент/компонент-ориентированного программирования. Я не хотел делать статью сильно большой и опять-же если тема будет востребована, я напишу и об этих парадигмах, возможно более подробно и с примерами кода.
- парадигмы программирования
- программирование
- императивное программирование
- структурное программирование
- процедурное программирование
- Веб-разработка
- Программирование
- Совершенный код
