Императивно срещу декларативно програмиране

В този момент несъмнено сте чували за наложителното програмиране срещу декларативното програмиране. Може би дори сте търсили какво всъщност означават тези термини.

За съжаление вероятно сте се сблъскали с определение, подобно на това: „Знаеш ли, императивното програмиране е като„ как “правиш нещо, а декларативното програмиране е по-скоро като„ какво “правиш или нещо.“

Това определение има идеален смисъл, след като действително знаете разликата между императивно и декларативно - но не го правите, поради което зададете въпроса на първо място. Това е като да се опиташ да отговориш „Какво е първо, пилето или яйцето?“, Освен че всички смятат, че пилето е направило, но дори не харесвате яйца и сте объркани.

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

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

Трудната част на тази тема е, както отбеляза Мерик, „Това е едно от онези неща, за които имаш интуиция, но не може да изглежда да обясни“.

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

Да се ​​върнем към първоначалната дефиниция, на която се подигравах: „Наложителното програмиране е като„ Как “правиш нещо, а декларативното програмиране е по-скоро като„ Какво “правиш.“ Тук всъщност се крие НЯКОЙ добра информация. Нека първо видим достойнството в това определение, като го извадим от контекста на програмирането и разгледаме пример за „реалния живот“.

Решавате, че сте прекарали твърде много време, спорейки за „JavaScript Fatigue” ™ и Реактивно функционално програмиране, а съпругът ви заслужава приятна среща. Решавате да отидете в Red Lobster, тъй като напоследък слушате много Бионсе. . Пристигате в Red Lobster, приближете се до рецепцията и кажете ...

Императивен подход (КАК): Виждам, че таблицата, разположена под знака Gone Fishin, е празна. Съпругът ми и аз ще отидем там и ще седнем.

Декларативен подход (КАКВО): Таблица за двама, моля.

Императивният подход е свързан с това как всъщност ще получите място. Трябва да изброите стъпките, за да можете да покажете КАК ще получите таблица. Декларативният подход се занимава повече с КАКВО искате, таблица за двама.

„Добре.“ - мозъкът ви

Още метафори!

Ще ви задам въпрос. Искам да помислите както за императивен отговор, така и за декларативен отговор.

„Аз съм от Wal-Mart. Как да стигна до къщата ти оттук? “

Императивен отговор: Излезте от северния изход на паркинга и вземете наляво. Качете се на I-15 на юг, докато стигнете до изхода за магистрала Бангертер. Тръгнете веднага на изхода, като отивате в Ikea. Отидете направо и вземете надясно при първата светлина. Продължете през следващата светлина, след което вземете следващата лява. Моята къща е №298.

Декларативен отговор: Адресът ми е 298 West Immutable Alley, Draper Utah 84020

Независимо от това как стигам до къщата ви, наистина има значение колата, която карам. Ще карам ли наложителна кола за смяна на пръчки или декларативна автоматична кола. Стига метафори?

Преди да се потопим в кода, важно е да осъзнаем, че много декларативни подходи имат някакъв императивен слой на абстракция. Вижте всички наши примери:

Декларативният отговор на служителя на Red Lobster е да приемем, че служителят на Red Lobster знае всички наложителни стъпки, за да ни отведе до масата. Познаването на адреса предполага, че имате някакъв вид GPS, който познава наложителните стъпки как да стигнете до къщата си.

Автоматичната кола има някакъв слой абстракция над превключване на предавките.

Това беше осъзнаването, което наистина го накара да щракне за мен, така че ще го повторя: Много (ако не всички) декларативни подходи имат някакъв основен императивен абстракция.

Ако това изречение има смисъл, вие се справяте чудесно!

Сега, ние ще се опитаме да направим скок от метафорична щастлива земя към истински свят код земя. За да направим скока по-грациозен, нека разгледаме някои програмни „езици“, които по своята същност са декларативни в сравнение с тези, които са наложителни по своята същност.

Императивно: C, C ++, Java
Декларативно: SQL, HTML
(Може да) Смесете: JavaScript, C #, Python

Помислете за типичния си пример за SQL или HTML,

ИЗБЕРЕТЕ * ОТ потребители, където е страна = „Мексико“;
<Статия>
  <Глава>
    

Декларативно програмиране

    

Поръсете Декларация във вашия словесен звук, за да звучи умно

  

Поглеждайки и в двата примера, вие имате много ясно разбиране за случващото се. И двамата са декларативни. Те са загрижени за това, което искате да направите, а не КАК искате да го направите.

Описвате това, което се опитвате да постигнете, без да инструктирате как да го направите. Изпълнението на подбора на всички потребители, които живеят в Мексико, беше абстрахирано от вас. Не се интересувате от това как уеб браузърът анализира статията ви и я показва на екрана. Вашето КАКВО е мексиканските потребители и ново заглавие и абзац на вашия уебсайт.

Дотук добре. Нека се потопим в по-практични примери за JavaScript.

Искам да се преструвате, че сега сте на техническо интервю и аз съм интервюиращият. Отворете конзолата си и отговорете на следните въпроси.

  1. Напишете функция, наречена double, която поема масив от числа и връща нов масив след удвояване на всеки елемент от този масив. двоен ([1,2,3]) -> [2,4,6]
  2. Напишете функция, наречена add, която поема масив и връща резултата от добавяне на всеки елемент в масива. добавете ([1,2,3]) -> 6
  3. Използвайки jQuery (или ванилов JavaScript), добавете обработващ събитие за щракване към елемента, който има идентификатор на „btn“. Когато щракнете, превключете (добавете или премахнете) класа „подчертаване“, както и променете текста на „Добавяне на подчертаване“ или „Премахване на подчертаване“ в зависимост от текущото състояние на елемента.

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

функция двойна (arr) {
  нека резултатите = []
  за (нека i = 0; i 

2.

функция add (arr) {
  нека резултат = 0
  за (нека i = 0; i 

3.

$ ("# btn"). щракнете (функция () {
  $ (Това) .toggleClass ( "връхна точка")
  $ (this) .text () === 'Добавяне на подчертаване "
    ? $ (this) .text ('Премахване на светлината »)
    : $ (this) .text ("Добавяне на подчертано")
})

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

  1. Най-очевидната обща е, че описват КАК да направят нещо. Във всеки пример ние или изрично повтаряме над масив, или изрично определяме стъпки за това как да реализираме функционалността, която искаме.
  2. Това може да не е толкова очевидно, ако не сте свикнали да мислите по „декларативния“ или още по-конкретно „функционалния“ начин. Във всеки пример ние мутираме част от състоянието (Ако не сте запознати с термина състояние, това е основно информация за нещо, което се съхранява в паметта - което трябва да звучи много като променливи.) В първите два примера създаваме променлива, наречена резултати и след това непрекъснато го променяме. В третия пример ние нямаме променливи, но все още имаме състояние, живеещо в самия DOM - ние променяме това състояние в DOM.
  3. Този е малко субективен, но за мен кодът по-горе не е много четим. Не мога просто да погледна кода и да разбера какво се случва. Моят мозък трябва да премине през кода точно както преводач, като същевременно отчита и контекста, в който живее кодът (друг негатив на изменяемите данни).

Добре, достатъчно ing на кода. Нека сега да разгледаме някои декларативни примери. Целта е да се отстранят всички проблеми отгоре. Затова всеки пример трябва да опише какво се случва, не може да мутира състояние и трябва да бъде четим от пръв поглед.

1.

функция двойна (arr) {
  връщане arr.map ((елемент) => елемент * 2)
}

2.

функция add (arr) {
  връщане arr.reduce ((prev, current) => prev + current, 0)
}

3.


    {This.state.buttonText}

Много по-добре

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

Във всеки пример описваме КАКВО искаме да се случи, а не КАК (не знаем КАК да се реализира карта и намаление, ние също не ни интересуват). Ние не мутираме никоя държава. Всички мутации се абстрахират от картата и намаляват. Освен това е и по-четена (след като свикнете, разбира се, да намалявате и намалявате).

А какво ще кажете за №3? Ами аз изневерих малко и използвам React - но обърнете внимание, че и трите императивни грешки все още са поправени. Истинската красота на React е, че можете да създадете тези декларативни потребителски интерфейси. Разглеждайки нашия Btn компонент, мога лесно да разбера как ще изглежда потребителският интерфейс. Друго предимство е вместо държавата да живее в DOM, тя живее в самия компонент React.

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

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

Едно нещо, което не отидох твърде далеч, е как функционалното програмиране е подмножество на декларативно програмиране. Ако още не сте, горещо препоръчвам да се запознаете по-подробно с функционалните техники за програмиране в JavaScript. Започнете с .map, .reduce, .filter и работете по пътя си от там.

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

С всичко казано, едно от любимите ми неща е да намеря определения в интернет, където създателят на споменатото определение се занимаваше повече с звучането интелигентно, отколкото от предоставянето на полезна, консумативна дефиниция. Императив срещу декларатив има ТО много добри примери за това. Наслади се!

Декларативното програмиране е „актът на програмиране на езици, които съответстват на менталния модел на разработчика, а не на оперативния модел на машината“.
Декларативното програмиране е програмиране с декларации, т.е. декларативни изречения.
Декларативното свойство е там, където може да съществува само един възможен набор от изрази, които могат да изразят всеки конкретен модулен семантичен. Императивното свойство е двойственото, при което семантиката е непоследователна по състав и / или може да бъде изразена с вариации на набори от изявления.
Декларативните езици контрастират с императивните езици, които уточняват изричното манипулиране на вътрешното състояние на компютъра; или процедурни езици, които определят изрична последователност от стъпки, които да следвате.
В компютърните науки декларативното програмиране е парадигма за програмиране, която изразява логиката на изчислението, без да описва контролния му поток.

Първоначално публикуван в tylermcginnis.com

Следвайте Тайлър МакГинис в Twitter

tylermcginnis.com