Функция которая позволяет изменить парадигму в python
Перейти к содержимому

Функция которая позволяет изменить парадигму в python

  • автор:

Кодим на Python по-функциональному: Познаем силу функциональной парадигмы программирования

Язык Python не зря пользуется популярностью в среде программистов Гугла и редакторов Хакера одновременно :). Этот поистине мощный язык позволяет писать код, следуя нескольким парадигмам, и сегодня мы попробуем разобраться, в чем же между ними разница и какой из них лучше следовать.

Какие парадигмы?! Давайте кодить!

Когда тебе надо написать что-то, то ты, наверное, меньше всего заморачиваешься относительно того, какую парадигму программирования выбрать. Скорее, ты либо выбираешь наиболее подходящий язык, либо сразу начинаешь кодить на своем любимом, предпочитаемом и годами проверенном. Оно и верно, пусть об идеологии думают идеологи, наше дело – программить :). И все-таки, программируя, ты обязательно следуешь какой-либо парадигме. Рассмотрим пример. Попробуем написать что-нибудь простое. ну, например, посчитаем площадь круга.

Можно написать так:

Площадь круга (вариант первый)

Площадь круга (вариант второй)

Можно и по-другому. но только как не старайся, код будет или императивным (как в первом случае), или объектно-ориентированным (как во втором).
Это происходит не из-за отсутствия воображения, а просто потому, что C++ «заточен» под эти парадигмы.

И лучшее (или худшее, в зависимости от прямоты рук), что с его помощью можно сделать – это смешать несколько парадигм.

Парадигмы

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

Императивное программирование

«Сначала делаем это, потом это, затем вот это»

Языки: Почти все

Абсолютно понятная любому программисту парадигма: «Человек дает набор инструкций машине».
С императивной парадигмы все начинают учить/понимать программирование.

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

«Считаем выражение и используем результат для чего-нибудь еще».

Языки: Haskell, Erlang, F#

Абсолютно непонятная начинающему программисту парадигма. Мы описываем не последовательность состояний (как в императивной парадигме), а последовательность действий.

Объектно-ориентированное программирование

«Обмениваемся сообщениями между объектами, моделируя взаимодействия в реальном мире».

Языки: Почти все

Объектно-ориентированная парадигма со своим появлением прочно вошла в нашу жизнь.
На ООП построены практически все современные бизнес-процессы.

Логическое программирование

«Отвечаем на вопрос поиском решения».

Языки: Prolog

Логическое программирование – довольно специфическая штука, но, в то же время, интересная и интуитивно понятная.
Достаточно простого примера:

В то время, как каждый программист по определению знаком с императивным и объектно-ориентированным программированием, с функциональным программированием в чистом виде мы сталкиваемся редко.

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

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

Функциональное программирование, наоборот, предусматривает последовательность действий над данными. Это сродни математике – мы долго пишем на доске формулу f(x), а потом подставляем x и получаем результат.

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

Двуликий питон

Нет лучшей теории, чем практика, так что давай уже что-нибудь напишем. А еще лучше – напишем на питоне :).
Посчитаем сумму квадратов элементов массива «data» императивно и функционально:

Императивный Питон

data = [. ]
sum = 0
for element in a:
sum += element ** 2
print sum

Функциональный Питон

data = [. ]
sq = lambda x: x**2
sum = lambda x,y: x+y
print reduce(sum, map(sq, data))

Оба примера на питоне, хотя я и не включил его в список функциональных языков. Это не случайность, поскольку полностью функциональный язык – довольно специфичная и редко используемая штука. Первым функциональным языком был Lisp, но даже он не был полностью функциональным (ставит в тупик, не правда ли?). Полностью функциональные языки используются для всякого рода научных приложений и пока не получили большого распространения.

Но если сами «функционалы» и не получили широкого распространения, то отдельные идеи перекочевали из них в скриптинговые (и не только) языки программирования.
Оказалось, что совершенно необязательно писать полностью функциональный код, достаточно украсить императивный код элементами функционального.

Питон в действии

Оказывается, концепции ФП реализованы в Питоне более чем изящно. Ознакомимся с ними подробнее.

?-исчисления

Lambda исчисления – это математическая концепция, которая подразумевает, что функции могут принимать в качестве аргументов и возвращать другие функции.
Такие функции называются функциями высших порядков. ?-исчисления основываются на двух операциях: аппликация и абстракция.
Я уже привел пример аппликации в предыдущем листинге. Функции map, reduce – это и есть те самые функции высших порядков, которые «апплицируют», или применяют, переданную в качестве аргумента функцию к каждому элементу списка (для map) или каждой последовательной паре элементов списка (для reduce).

Что касается абстракции – здесь наоборот, функции создают новые функции на основе своих аргументов.

Lambda-абстракция

def add(n):
return lambda x: x + n

adds = [add(x) for x in xrange(100)]

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

Замыкание – это определение функции, зависящей от внутреннего состояния другой функции. В нашем примере это lambda x. С помощью этого приема мы делаем что-то похожее на использование глобальных переменных, только на локальном уровне.

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

Таким образом, мы можем написать код, который работает не только с переменными, но и функциями, что дает нам еще несколько «степеней свободы».

Чистые функции и ленивый компилятор

Императивные функции могут изменять внешние (глобальные) переменные, и это значит, что функция может возвращать различные значения при одних и тех же значениях аргумента на разных стадиях выполнения программы.

Такое утверждение совсем не подходит для функциональной парадигмы. Здесь функции рассматриваются как математические, зависящие только от аргументов и других функций, за что они и получили прозвище «чистые функции».

Как мы уже выяснили, в функциональной парадигме можно распоряжаться функциями как угодно. Но больше всего выгоды мы получаем, когда пишем «чистые функции». Чистая функция – это функция без побочных эффектов, а значит, она не зависит от своего окружения и не изменяет его состояния.

Применение чистых функций дает нам ряд преимуществ:

  • Во-первых, если функции не зависят от переменных окружения, то мы уменьшаем количество ошибок, связанных с нежелательными значениями этих самых переменных. Вместе с количеством ошибок мы уменьшаем и время отладки программы, да и дебагить такие функции гораздо проще.
  • Во-вторых, если функции независимы, то компилятору есть, где разгуляться. Если функция зависит только от аргументов, то ее можно посчитать только один раз. В следующие разы можно использовать кэшированное значение. Также, если функции не зависят друг от друга, их можно менять местами и даже автоматически распараллеливать.

Для увеличения производительности в ФП также используются ленивые вычисления. Яркий пример:

print length([5, 4/0, 3+2])

По идее, на выходе мы должны получить ошибку деления на ноль. Но ленивый компилятор питона просто не станет вычислять значения каждого элемента списка, так как его об этом не просили. Нужна длина списка – пожалуйста!
Те же принципы используются и для других языковых конструкций.

В результате несколько «степеней свободы» получает не только программист, но и компилятор.

Списочные выражения и условные операторы

Чтобы жизнь (и программирование) не казались тебе медом, разработчики питона придумали специальный «подслащающий» синтаксис, который буржуи так и называют – «syntactic sugar».
Он позволяет избавиться от условных операторов и циклов… ну, если не избавиться, то уж точно свести к минимуму.

В принципе, ты его уже видел в предыдущем примере – это adds = [add(x) for x in xrange(100)]. Здесь мы сразу создаем и инициализируем список значениями функций. Удобно, правда?
Еще есть такая штука, как операторы and и or, которые позволяют обходиться без громоздких конструкций типа if-elif-else.

Таким образом, с помощью инструментария питона можно превратить громоздкий императивный кусок кода в красивый функциональный.

Императивный код

L = []
for x in xrange(10):
if x % 2 == 0:
if x**2>=50:
L.append(x)
else:
L.append(-x)
print L

Функциональный код

print [x**2>=50 and x or -x for x in xrange(10) if x%2==0]

Итоги

Как ты уже понял, необязательно полностью следовать функциональной парадигме, достаточно умело использовать ее в сочетании с императивной, чтобы упростить себе жизнь. Однако, я все время говорил про императивную парадигму. и ничего не сказал про ООП и ФП.

Что ж, ООП – это, фактически, надстройка над императивной парадигмой, и если ты перешел от ИП к ООП, то следующим шагом должно быть применение ФП в ООП. В заключение скажу пару слов об уровне абстракции. Так вот, чем он выше – тем лучше и именно сочетание ООП и ФП дает нам этот уровень.

CD

На диск я положил свежие дистрибутивы питона для виндусоидов. Линуксоидам помощь не нужна :).

WWW

Несколько хороших ресурсов для тех, кому хочется узнать больше:

  • http://www.python.org
  • http://en.wikipedia.org/wiki/Programming_paradigm
  • http://www.ibm.com/developerworks/library/l-prog.html

INFO

Если тебе не приглянулся питон, то не расстраивайся – ты можешь успешно применять идеи функционального программирования и в других языках высокого уровня.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *