Функционално кодиране в PythonИзследване на силата на парадигмата на функционалното програмиране

Съдържанието на статията

Какви парадигми?! Да кодираме!

Когато трябва да напишете нещо, вероятно най-малко се притеснявате коя програмна парадигма да изберете. По-скоро или избирате най-подходящия език, или веднага започвате да кодирате на вашия любим, предпочитан и доказал се през годините. Вярно е, нека идеолозите да мислят за идеология, нашата работа е да програмираме :). И все пак, програмирането, вие винаги следвате една парадигма. Помислете за пример. Нека се опитаме да напишем нещо просто... добре, нека изчислим площта на кръг, например.

Можете да напишете така:

Площ на кръг (първи вариант)

double area_of_circle(double r) return M_PI*pow(r,2); > int main() двойно r = 5; вън

Кръгова зона (втора опция)

клас Circle < двойно r; публичен: Кръг (двойно r) < това->r = r; > двойна област() < връщане на M_PI*pow(this->r,2); > void print_area() cout area() print_area();>

Възможно е и по друг начин ... но колкото и да се опитвате, кодът ще бъде или императивен (както в първия случай) или обектно-ориентиран (както във втория). Това не се дължи на липса на въображение, а просто защото C++ е „пригоден“ за тези парадигми.

И най-доброто (или най-лошото, в зависимост от правотата на ръцете), което можете да направите с него, е да смесите няколко парадигми.

Както може би се досещате, един и същи език може да бъде написан по няколко парадигми, а понякога дори по няколко наведнъж. Нека разгледаме основните им представители, защото без тези знания никога няма да можете да се считате за професионален кодер и най-вероятно няма да знаете и за работа в екип.ще трябва да забравя.

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

„Първо направете това, след това това, след това това“

Езици: почти всички

Абсолютно разбираема за всеки програмист парадигма: „Човекът дава набор от инструкции на машината“. Всеки започва да учи/разбира програмирането от императивната парадигма.

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

„Ние четем израза и използваме резултата за нещо друго.“

Езици: Haskell, Erlang, F#

Парадигма, която е абсолютно неразбираема за начинаещ програмист. Ние описваме не последователност от състояния (както в императивната парадигма), а последователност от действия.

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

„Ние обменяме съобщения между обекти, симулирайки взаимодействия в реалния свят.“

Езици: почти всички

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

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

„Ние отговаряме на въпроса, като намерим решение.“

Езици: Пролог

Логическото програмиране е доста специфично нещо, но в същото време интересно и интуитивно. Един прост пример ще бъде достатъчен:

Докато всеки програмист по дефиниция е запознат с императивното и обектно-ориентираното програмиране, чистото функционално програмиране е рядкост.

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

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

Функционалното програмиране, напротив, предвижда последователност от действия върху данните. Това е като математиката - ние сме дългинапишете формулата f(x) на дъската и след това заместете x и получете резултата.

И целият смисъл на функционалното програмиране е, че тук формулата е инструмент, който прилагаме към x.

двулик питон

Няма по-добра теория от практиката, така че нека напишем нещо вече. И още по-добре - пишете на python :). Нека изчислим сумата от квадратите на елементите на масива "данни" императивно и функционално:

Императивен Python

данни = [. ] сума = 0 за елемент в a: сума += елемент ** 2 печатна сума

Функционален Python

данни = [. ] sq = ламбда x: x**2 сума = ламбда x,y: x+y отпечатайте намаление (сума, карта (квадрат, данни))

И двата примера са в Python, въпреки че не го включих в списъка с функционални езици. Това не е случайно, тъй като напълно функционалният език е доста специфично и рядко използвано нещо. Първият функционален език беше Lisp, но дори и той не беше напълно функционален (объркващо, нали?). Напълно функционалните езици се използват за всякакви научни приложения и все още не са широко използвани.

Но ако самите „функционали“ не бяха широко използвани, тогава някои идеи мигрираха от тях към скриптови (и не само) езици за програмиране. Оказа се, че не е необходимо да се пише напълно функционален код, достатъчно е да се украси императивният код с функционални елементи.

Python в действие

Оказва се, че концепциите на FP са имплементирани в Python по повече от елегантен начин. Нека се запознаем с тях по-подробно.

?-считане

Ламбда смятането е математическа концепция, която предполага, че функциите могат да приемат като аргументи и да връщат други функции. Такива функции се наричат ​​функции от по-висок ред.?-изчислението се основава на две операции: приложение и абстракция. Вече дадох пример за приложение в предишния списък. Функциите за карта, намаляване са функциите от много по-висок ред, които „прилагат“ или прилагат функцията, предавана като аргумент към всеки елемент от списъка (за карта) или всяка последователна двойка елементи от списък (за намаляване).

Що се отнася до абстракцията, обратното е, функциите създават нови функции въз основа на техните аргументи.

Ламбда абстракция

def add(n): връща ламбда x: x + n

adds = [add(x) за x в xrange(100)]

Тук създадохме списък с функции, всяка от които добавя определено число към аргумента. Този малък пример също съдържа няколко интересни дефиниции на функционално програмиране - затваряне и къри.

Затварянето е дефиниция на функция, която зависи от вътрешното състояние на друга функция. В нашия пример това е ламбда x. С тази техника правим нещо подобно на използването на глобални променливи, само на локално ниво.

Curry е трансформирането на функция от двойка аргументи във функция, която приема своите аргументи един по един. Това е, което направихме в примера, само че получихме масив от такива функции веднага.

Така можем да пишем код, който работи не само с променливи, но и с функции, което ни дава още няколко „степени на свобода“.

Чисти функции и мързелив компилатор

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

Подобно твърдение изобщо не отговаря на функционалната парадигма. Тук функциите се разглеждат катоматематически, зависещи само от аргументи и други функции, за които получиха прозвището "чисти функции".

Както вече разбрахме, във функционалната парадигма можете да разполагате с функции, както искате. Но получаваме най-голяма полза, когато пишем "чисти функции". Чистата функция е функция без странични ефекти, което означава, че не зависи от околната среда и не променя състоянието си.

Използването на чисти функции ни дава редица предимства:

  • Първо, ако функциите не зависят от променливите на средата, тогава намаляваме броя на грешките, свързани с нежеланите стойности на същите тези променливи. Заедно с броя на грешките, намаляваме и времето за отстраняване на грешки на програмата, а отстраняването на грешки на такива функции е много по-лесно.
  • Второ, ако функциите са независими, тогава компилаторът има къде да се разхожда. Ако една функция зависи само от аргументи, тогава тя може да бъде оценена само веднъж. Следващият път можете да използвате кешираната стойност. Също така, ако функциите не зависят една от друга, те могат да бъдат разменени и дори автоматично паралелизирани.

FP също използва мързелива оценка за подобряване на производителността. Ярък пример:

дължина на печат ([5, 4/0, 3+2])

На теория на изхода трябва да получим грешка при деление на нула. Но мързеливият компилатор на Python просто няма да изчисли стойността на всеки елемент от списъка, защото не е бил помолен. Необходима е дължина на списъка - моля! Същите принципи важат и за други езикови конструкции.

В резултат на това не само програмистът, но и компилаторът получава няколко "степени на свобода".

Избройте изрази и условни оператори

За да не ви изглежда животът (и програмирането) като мед, разработчиците на python измислиха специален "подслаждащ" синтаксис,които буржоазните наричат ​​„синтактична захар“. Позволява ви да се отървете от условни оператори и цикли ... е, ако не да се отървете, то със сигурност да ги минимизирате.

По принцип вече го видяхте в предишния пример - това е adds = [add(x) for x in xrange(100)]. Тук веднага създаваме и инициализираме списъка с функционални стойности. Удобно, нали? Има и такова нещо като операторите и и или, които ви позволяват да правите без тромави конструкции като if-elif-else.

По този начин, с помощта на инструментариума на Python, можете да превърнете обемист императивен код в красив функционален.

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

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

Функционален код

print [x**2>=50 и x или -x за x в xrange(10), ако x%2==0]

Както вече разбрахте, не е необходимо да следвате напълно функционалната парадигма, достатъчно е умело да я използвате в комбинация с императивната, за да опростите живота си. Въпреки това през цялото време говорих за императивната парадигма ... и не казах нищо за OOP и FP.

Е, ООП всъщност е добавка към императивната парадигма и ако сте преминали от IP към ООП, тогава следващата стъпка трябва да бъде да използвате FP в ООП. В заключение ще кажа няколко думи за нивото на абстракция. И така, колкото по-високо е то, толкова по-добре и именно комбинацията от OOP и FP ни дава това ниво.

Сложих свежи дистрибуции на питон за Windowsoid на диска. Потребителите на Linux нямат нужда от помощ :).

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

Ако не харесвате Python, тогава не се обезсърчавайте - можете успешно да приложите идеите на функционалното програмиране в други езици от високо ниво.