Кодим на Python по-функциональному: Познаем силу функциональной парадигмы программированияЯзык Python не зря пользуется популярностью в среде программистов Гугла и редакторов Хакера одновременно :). Этот поистине мощный язык позволяет писать код, следуя нескольким парадигмам, и сегодня мы попробуем разобраться, в чем же между ними разница и какой из них лучше следовать. Какие парадигмы?! Давайте кодить!Когда тебе надо написать что-то, то ты, наверное, меньше всего заморачиваешься относительно того, какую парадигму программирования выбрать. Скорее, ты либо выбираешь наиболее подходящий язык, либо сразу начинаешь кодить на своем любимом, предпочитаемом и годами проверенном. Оно и верно, пусть об идеологии думают идеологи, наше дело – программить :). И все-таки, программируя, ты обязательно следуешь какой-либо парадигме. Рассмотрим пример. Попробуем написать что-нибудь простое... ну, например, посчитаем площадь круга. Можно написать так: Площадь круга (вариант первый) А можно и так: Площадь круга (вариант второй) Можно и по-другому... но только как не старайся, код будет или императивным (как в первом случае), или объектно-ориентированным (как во втором). Это происходит не из-за отсутствия воображения, а просто потому, что C++ «заточен» под эти парадигмы. И лучшее (или худшее, в зависимости от прямоты рук), что с его помощью можно сделать – это смешать несколько парадигм. ПарадигмыКак ты уже догадался, на одном и том же языке можно писать, следуя нескольким парадигмам, причем иногда даже нескольким сразу. Рассмотрим основные их представители, ведь без этих знаний ты никогда не сможешь считать себя профессиональным кодером, да и о работе в команде тебе тоже, скорее всего, придется забыть. Императивное программирование«Сначала делаем это, потом это, затем вот это» Языки: Почти все Абсолютно понятная любому программисту парадигма: «Человек дает набор инструкций машине». С императивной парадигмы все начинают учить/понимать программирование. Функциональное программирование«Считаем выражение и используем результат для чего-нибудь еще». Языки: Haskell, Erlang, F# Абсолютно непонятная начинающему программисту парадигма. Мы описываем не последовательность состояний (как в императивной парадигме), а последовательность действий. Объектно-ориентированное программирование«Обмениваемся сообщениями между объектами, моделируя взаимодействия в реальном мире». Языки: Почти все Объектно-ориентированная парадигма со своим появлением прочно вошла в нашу жизнь. Логическое программирование«Отвечаем на вопрос поиском решения». Языки: Prolog Логическое программирование – довольно специфическая штука, но, в то же время, интересная и интуитивно понятная.
В то время, как каждый программист по определению знаком с императивным и объектно-ориентированным программированием, с функциональным программированием в чистом виде мы сталкиваемся редко. Функциональное программирование противопоставляют императивному. Императивное программирование подразумевает последовательность изменений состояния программы, а переменные служат для хранения этого состояния. Функциональное программирование, наоборот, предусматривает последовательность действий над данными. Это сродни математике – мы долго пишем на доске формулу f(x), а потом подставляем x и получаем результат. И вся соль функционального программирования в том, что здесь формула – это инструмент, который мы применяем к иксу. Двуликий питонНет лучшей теории, чем практика, так что давай уже что-нибудь напишем. А еще лучше – напишем на питоне :). Посчитаем сумму квадратов элементов массива «data» императивно и функционально: Императивный Питон
Функциональный Питон Оба примера на питоне, хотя я и не включил его в список функциональных языков. Это не случайность, поскольку полностью функциональный язык – довольно специфичная и редко используемая штука. Первым функциональным языком был Lisp, но даже он не был полностью функциональным (ставит в тупик, не правда ли?). Полностью функциональные языки используются для всякого рода научных приложений и пока не получили большого распространения. Но если сами «функционалы» и не получили широкого распространения, то отдельные идеи перекочевали из них в скриптинговые (и не только) языки программирования. Оказалось, что совершенно необязательно писать полностью функциональный код, достаточно украсить императивный код элементами функционального. Питон в действииОказывается, концепции ФП реализованы в Питоне более чем изящно. Ознакомимся с ними подробнее. ?-исчисленияLambda исчисления – это математическая концепция, которая подразумевает, что функции могут принимать в качестве аргументов и возвращать другие функции. Такие функции называются функциями высших порядков. ?-исчисления основываются на двух операциях: аппликация и абстракция. Я уже привел пример аппликации в предыдущем листинге. Функции map, reduce – это и есть те самые функции высших порядков, которые «апплицируют», или применяют, переданную в качестве аргумента функцию к каждому элементу списка (для map) или каждой последовательной паре элементов списка (для reduce). Что касается абстракции – здесь наоборот, функции создают новые функции на основе своих аргументов. Lambda-абстракция
Здесь мы создали список функций, каждая из которых прибавляет к аргументу определенное число. В этом маленьком примерчике также уместилась еще пара интересных определений функционального программирования – замыкание и карринг. Замыкание – это определение функции, зависящей от внутреннего состояния другой функции. В нашем примере это lambda x. С помощью этого приема мы делаем что-то похожее на использование глобальных переменных, только на локальном уровне. Карринг – это преобразование функции от пары аргументов в функцию, берущую свои аргументы по одному. Что мы и сделали в примере, только у нас получился сразу массив таких функций. Таким образом, мы можем написать код, который работает не только с переменными, но и функциями, что дает нам еще несколько «степеней свободы». Чистые функции и ленивый компиляторИмперативные функции могут изменять внешние (глобальные) переменные, и это значит, что функция может возвращать различные значения при одних и тех же значениях аргумента на разных стадиях выполнения программы. Такое утверждение совсем не подходит для функциональной парадигмы. Здесь функции рассматриваются как математические, зависящие только от аргументов и других функций, за что они и получили прозвище «чистые функции». Как мы уже выяснили, в функциональной парадигме можно распоряжаться функциями как угодно. Но больше всего выгоды мы получаем, когда пишем «чистые функции». Чистая функция – это функция без побочных эффектов, а значит, она не зависит от своего окружения и не изменяет его состояния. Применение чистых функций дает нам ряд преимуществ:
Для увеличения производительности в ФП также используются ленивые вычисления. Яркий пример:
По идее, на выходе мы должны получить ошибку деления на ноль. Но ленивый компилятор питона просто не станет вычислять значения каждого элемента списка, так как его об этом не просили. Нужна длина списка – пожалуйста! Те же принципы используются и для других языковых конструкций. В результате несколько «степеней свободы» получает не только программист, но и компилятор. Списочные выражения и условные операторыЧтобы жизнь (и программирование) не казались тебе медом, разработчики питона придумали специальный «подслащающий» синтаксис, который буржуи так и называют – «syntactic sugar». В принципе, ты его уже видел в предыдущем примере – это adds = [add(x) for x in xrange(100)]. Здесь мы сразу создаем и инициализируем список значениями функций. Удобно, правда? Таким образом, с помощью инструментария питона можно превратить громоздкий императивный кусок кода в красивый функциональный. Императивный код Функциональный код ИтогиКак ты уже понял, необязательно полностью следовать функциональной парадигме, достаточно умело использовать ее в сочетании с императивной, чтобы упростить себе жизнь. Однако, я все время говорил про императивную парадигму... и ничего не сказал про ООП и ФП. Что ж, ООП – это, фактически, надстройка над императивной парадигмой, и если ты перешел от ИП к ООП, то следующим шагом должно быть применение ФП в ООП. В заключение скажу пару слов об уровне абстракции. Так вот, чем он выше – тем лучше и именно сочетание ООП и ФП дает нам этот уровень. CDНа диск я положил свежие дистрибутивы питона для виндусоидов. Линуксоидам помощь не нужна :). WWWНесколько хороших ресурсов для тех, кому хочется узнать больше:
INFOЕсли тебе не приглянулся питон, то не расстраивайся – ты можешь успешно применять идеи функционального программирования и в других языках высокого уровня. |