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


24

Мені дуже комфортно з імперативним програмуванням. У мене ніколи не виникає проблем з алгоритмічним вираженням того, що я хочу зробити на комп’ютері, як тільки я зрозумів, що це хочу зробити. Але якщо мова йде про такі мови, як SQL або я часто застрягаю, тому що моя голова занадто звикла до імперативного програмування.

Наприклад, припустимо, що у вас є група відносин (bandName, bandCountry), місце проведення (venueName, venueCountry), п'єси (bandName, venueName), і я хочу написати запит, який говорить: всі місця імені такі, що для кожної групиCountry є група з країна, яка грає на місці цієї назви.

Приклад: Я хочу всіх імен місця, в яких грали групи з усіх країн (bandCountry). Також під "відношенням" я маю на увазі таблицю SQL.

На моїй думці я негайно переходжу "для кожного місця ім'я, перейдіть на всі групи BandCountries, і для кожної групиCountry отримайте список гуртів, що виходять з нього. Якщо жодна з них не грає на місці знаменитості, перейдіть до наступного місця ім'я. Інакше, в кінці гуртуCountries ітерація додає venueName до набору хороших імен venueNames ".

... але ти не можеш так говорити в SQL, і мені насправді потрібно думати, як це сформулювати, при цьому інтуїтивне імперативне рішення постійно нудить в потилиці. Хтось ще мав цю проблему? Як ти це подолав? Ви з'ясували зміну парадигми? Зробили карту від імперативних концепцій до концепцій SQL, щоб перевести імперативні рішення в декларативні? Читати хорошу книгу?

PS Я не шукаю рішення для вищезазначеного запиту, я його вирішував.


1
Це гарне запитання, оскільки ви озвучуєте слабкість, яку має багато (в тому числі і я).
Девід Вейзер

Можливо, буде корисно визначити, що ви маєте на увазі під "відношенням" у своєму питанні. У реляційній моделі (математика позаду SQL) "відношення" приблизно аналогічно таблиці SQL. Дуже багато людей скажуть "відношення", коли вони справді мають на увазі сказати "відносини".
Джейсон Бейкер

Вивчіть теорію множин та дискретну математику.

1
@ Jase21, я особисто добре знайомий з обома, але нетривіальні речі в SQL все ще почуваються смішними. Жоден із чистих прикладів математики не стосується дивних реальних речей. Крім того, можна використовувати LINQ і, таким чином, не заважати SQL. Нарешті, до запитувача: ви звикнете до цього з часом.
робота

Відповіді:


12

Ідея декларативної справи полягає в тому, що ви повинні вказати що , а не як .

Мені це здається, що ти на правильному шляху. Проблема не в тому, що ви думаєте про речі не так. Це те, що ти йдеш занадто далеко. Давайте розглянемо, що ви намагаєтеся зробити:

Наприклад, припустимо, що у вас є група відносин (bandName, bandCountry), місце проведення (venueName, venueCountry), п'єси (bandName, venueName), і я хочу написати запит, який говорить: всі місця імені такі, що для кожної групиCountry є група з країна, яка грає на місці цієї назви.

Поки що це чудово. Але тоді ви робите це:

На моїй думці я негайно переходжу "для кожного місця ім'я, перейдіть на всі групи BandCountries, і для кожної групиCountry отримайте список гуртів, що виходять з нього. Якщо жодна з них не грає на місці знаменитості, перейдіть до наступного місця ім'я. Інакше, в кінці гуртуCountries ітерація додає venueName до набору хороших імен venueNames ".

По суті, ти робиш непотрібну роботу. Ви знаєте, чого хочете, а це все, що вам справді потрібно. Але далі ви продовжуєте і намагаєтесь розібратися, як це отримати.

Якби я був ти, я б спробував увійти в таку звичку:

  1. Визначте, що ви хочете.
  2. Свідомо зупиняйте себе від визначення того, як його отримати.
  3. З'ясуйте, як представити те, що ви хочете в SQL.

Це може зайняти певний час і зусилля з вашого боку, але, як тільки ви дійсно захоплюєтеся декларативним програмуванням, воно стає дуже корисним. Насправді ви можете виявити, що ви використовуєте декларативне програмування в іншій частині коду.

Якщо ви шукаєте книгу, я рекомендую SQL та реляційну теорію . Це дійсно допомагає зрозуміти теорію баз даних SQL. Просто пам’ятайте, щоб прийняти рекомендації Дати з зерном солі. Він дає дуже гарну інформацію, але часом може бути трохи впевненим.


Я не розумію, як зрозуміти, як щось отримати - це неправильний підхід. Не має значення, якою мовою ви користуєтесь, ви повинні з'ясувати, як сказати їй робити те, що ви хочете.
davidk01

9

мислити з точки зору множин, а не ітераторів; оператори sql визначають властивості потрібного набору вихідних даних (він же таблиця / відношення)

всі імена місця, такі, що для кожного гуртуКонтарт є група з тієї країни, яка грає на місці цього імені

Результатом цього (якщо я правильно зрозумів ваші наміри!) буде набір місць, що мають принаймні одну групу, яка грає в цьому місці. Ітерація над bandCountry непотрібна, оскільки відношення PLAYS вже містить інформацію, яку ви шукаєте, вам просто потрібно усунути дублікати

тож у SQL це було б:

select 
    distinct venueName
from PLAYS

РЕДАКТУВАТИ: нормально, тому бажаний фактичний набір трохи складніше. База даних запитує: на яких майданчиках розміщуються групи з усіх країн?

Отже, ми визначаємо критерії членства для елемента бажаного набору як мети, а потім працюємо назад, щоб заповнити набір. Місце проведення є членом набору результатів, якщо він має рядок PLAYS щонайменше для одного діапазону з кожної країни. Як ми отримуємо цю інформацію?

Один із способів - підрахувати різні країни для кожного місця і порівняти їх з кількістю всіх країн. Але ми не маємо відношення до КРАЇНИ. Якщо ми замислимось над моделлю, заданою на мить, ми бачимо, що набір усіх країн не є правильними критеріями; це безліч усіх країн, що мають принаймні одну смугу. Таким чином, нам не потрібна таблиця країни (хоча для нормалізованої моделі у нас вона повинна бути), і нам не байдуже країну місця проведення, ми можемо просто порахувати країни, у яких є групи, наприклад (у MS-SQL )

declare @BandCountryCount int
select
    @BandCountryCount = COUNT(distinct bandCountry)
from BAND

Ми можемо порахувати країни гурту для кожного місця

select
    P.venueName, COUNT(distinct B.bandCountry) as VenueBandCountryCount
from PLAYS P
    inner join BAND B on B.bandName = P.bandName

і ми можемо з'єднати ці два разом за допомогою підпиту

select
    venueName
from (
    select
        P.venueName, COUNT(distinct B.bandCountry) as VenueBandCountryCount
    from PLAYS P
        inner join BAND B on B.bandName = P.bandName
) X
where X.VenueBandCountryCount = @BandCountryCount

Тепер це не найкрасивіший можливий запит (GROUP BY і HAVING можна вважати більш «елегантним» рішенням, ніж тимчасові змінні та підзапит), але це досить очевидно, за чим ми підемо, тому ми залишимо це на користь ОП .

Метою ОП було навчитися перемикати мислення з імперативного на декларативне. З цією метою подивіться, що робило описане імперативне рішення:

для кожного місцяНазвіть назву для всіх груп країн і для кожної групи країн отримайте список гуртів, що виходять з нього. Якщо жоден з них не грає в venueName, перейдіть до наступного ім'яName. В іншому випадку, в кінці діапазону ітерація країн додає venueName до набору хороших місць знамення

Які визначальні критерії у вищезазначеному? Я думаю це:

... Якщо жоден з них [набір гуртів з певної країни] не грає на місці проведення імені ...

Це критерії дискваліфікації . Імперативний процес думки починається з повного відра і викидає речі, що не відповідають критеріям. Ми фільтруємо дані.

Це добре для простих речей, але це допомагає мислити з точки зору побудови потрібного набору результатів; які відповідні кваліфікаційні критерії дозволяють замість цього заповнити відро?

  • дискваліфікатор: якщо немає групи з групиCountry, яка грає в місці, місце проведення дискваліфікується
  • (частковий) класифікатор: якщо принаймні одна група з bandCountry грає на місці, то місце може бути нормальним; продовжуйте перевіряти решту груп країн
  • (повний) класифікатор: якщо принаймні один гурт з кожного гуртуCountry грає на місці, то місце проведення кваліфікації

Кінцевий класифікатор можна спростити за допомогою підрахунків: bandCountry "задоволений", якщо принаймні одна група звідти грає на місці; кількість "задоволених" країн гурту для місця проведення має бути рівним кількості країн групи для проведення місця проведення кваліфікації.

Тепер ми можемо міркувати між відносинами за допомогою навігації:

  • Почніть з відношення VENUE [нам це не потрібно для відповіді, але це концептуальна відправна точка для реляційної навігації]
  • приєднайтесь до PLAYS на venueName
  • приєднайтесь до BAND на bandName, щоб отримати bandCountry
  • нас не хвилює назва гурту; виберіть лише місце імені та назву смуги
  • нас не хвилює зайві групи країн; усуньте дублікати за допомогою DISTRICT або GROUP BY
  • нас хвилює лише кількість чітких країн, а не назви
  • ми хочемо лише місць, де кількість чітких країн групи збігається з загальною кількістю bandCountries

що призводить назад до рішення вище (або його розумного факсимільного опису)

ПІДСУМОК

  • теорія множин
  • реляційні навігаційні шляхи
  • включно проти ексклюзивних критеріїв (від кваліфікації до дискваліфікації)

Це фактично "набір майданчиків, які в них грають групи з усіх країн (bandCountry> = venueCountry)".
EpsilonVector

@EpsilonVector: див. Правки
Стівен А. Лоу

4

Один із способів навчитися мислити та програмувати в декларативному стилі - це вивчити мову масиву загального призначення, наприклад APL або J. SQL, мабуть, не найкращий засіб для того, щоб навчитися програмувати декларативно. У APL або J ви навчитесь керувати цілими масивами (векторами, матрицями чи масивами вищого рангу), без явного циклічного циклу чи ітерації. Це значно полегшує розуміння SQL та реляційної алгебри. Як дуже простий приклад, щоб вибрати елементи з вектора V, значення якого більше 100, в APL пишемо:

(V>100)/V

Тут V> 100 оцінює булевий масив тієї ж форми, що і V, з позначенням 1 значень, які ми хочемо зберегти. Для досвідченого APLer не трапляється, що відбувається ітерація, ми просто наносимо маску на вектор V, повертаючи новий вектор. Зрозуміло, це концептуально те, що робить SQL, де обмежує дію клаузула або реляційна алгебра.

Я не думаю, що ви можете добре впоратися з декларативним програмуванням, не роблячи багато цього, і SQL, як правило, занадто специфічний. Потрібно написати багато коду загального призначення, навчившись робити без циклів та структури if / then / else та весь апарат, який відвідує імперативні, процедурні та скалярні програми.

Можливо, існують й інші функціональні мови, які також допомагають у такому способі мислення, але мови масивів дуже близькі до SQL.


+1 для "[ви не можете] отримати хороший хід ... не роблячи багато цього". Ніхто не навчився імперативного програмування (з його явно контр-інтуїтивно зрозумілими конструкціями a = a + 1) також протягом ночі. Для вивчення декларативних стилів, таких як логіка, функціонал тощо, потрібен час, як і потрібен час, щоб вивчити імперативне програмування.
ПРОСТО МОЕ правильне ДУМКУ

1

По-перше, ви повинні навчитися обом. Ви можете мати перевагу, але працюючи в районах, де інший кращий, не боріться з цим. Багато програмістів спокушаються використовувати курсори у реляційних базах даних, оскільки вони настільки звичні для переходу кожного запису, але база даних набагато краща в наборах. Ви не хочете вдаватись до думки "Я знаю, як це зробити, і я маю найбільше контролю, бла, бла, бла".


1

Ви вчитеся мислити декларативно так, як навчились мислити імперативно: на практиці починаючи з більш простих проблем і працюючи над тим, як ви «отримуєте це».

Ваш перший досвід із імперативним програмуванням включав цілу купу протиінтуїтивних (і, власне, зовсім смішних) тверджень на кшталт " a = a + 1". Ви обернули це своєю думкою до того, що зараз ви, мабуть, навіть не згадуєте, що відступили від очевидної неправди твердження. Ваша проблема з декларативними стилями полягає в тому, що ви повернулися туди, де були, коли ви вперше почали користуватися імперативними стилями: "незрозумілим новичком". Ще гірше, що у вас є багато років практики з одним стилем, який суперечить цьому новому стилю і маєте роки звичок скасовувати - як звичка "контролювати будь-яку ціну".

Декларативні стилі працюють з іншим підходом, до якого зараз вам не вистачає інтуїції (якщо ви протягом багатьох років не зберігали ваші навички математики дуже гостро - чого цього немає в більшості людей). Ви повинні вивчити, як думати, і єдиний спосіб перевчитися - це зробити, простий крок за один раз.

Вибір SQL як вашого першого кроку в декларативне програмування може бути помилкою, якщо ви дійсно хочете вивчити ці поняття. Впевнений, чисельність кортежу, на якій ґрунтується, є такою ж декларативною, як це стає, але, на жаль, чистота обчислення кортежу була погано зірвана реаліями впровадження, і мова, фактично, стала дещо заплутаною. Ви можете замість цього поглянути на інші більш корисні (у тому сенсі, якими ви звикли) декларативні мови, такі як Lisps (особливо схема ), Haskell та MLs для (в основному) функціонального програмування або, як альтернатива, Prolog і Mercury для (переважно) логічне програмування.

Вивчення цих інших мов дасть вам краще розуміння, як працює декларативне програмування з кількох причин:

  1. Вони корисні для програмування «від колиски до могили» - так як ви можете написати повну програму цими мовами від початку до кінця. Вони корисні стоячи окремо, на відміну від SQL, який справді є досить марним для більшості людей як автономна мова.

  2. Кожен з них дає вам різний нахил щодо декларативного програмування, який може дати вам різні дороги, щоб, нарешті, "отримати його".

  3. Вони також дають вам різний нахил щодо роздумів про програмування взагалі. Вони покращать вашу здатність міркувати про проблеми та кодування, навіть якщо ви ніколи не використовуєте їх безпосередньо самостійно.

  4. Уроки, які ви засвоїте з них, допоможуть вам і з вашим SQL - особливо, якщо ви розібраєтеся з обчисленням кортежу за реляційними базами даних для чистої форми роздумів про дані.

Я особливо рекомендую вивчити одну з функціональних мов ( Clojure , як один з Lisps, мабуть, хороший вибір тут) і одну з логічних мов (мені найбільше подобається Меркурій, але Prolog має набагато більше корисного матеріалу для вивчення) для максимального розширення роздумного процесу.


1

Неправильно мислити в обов'язковому порядку в таких декларативних умовах, як SQL. Просто імперативне мислення має відбуватися на дещо вищому рівні, ніж те, що ви описали. Щоразу, коли мені потрібно запитувати базу даних, яка використовує SQL, я завжди думаю про себе:

  • Ось шматочки, які мені потрібні.
  • Я збираюся їх скласти таким чином.
  • Я збираюся знищити те, що тільки що отримав, за допомогою наступних предикатів, щоб дійти до того, що я насправді шукаю.

Вищезазначений алгоритм імперативного рівня високого рівня, і він працює досить добре для мене в налаштуваннях SQL. Я думаю, що це вважається підходом зверху вниз, і Стівен А. Лоу описує досить хороший підхід знизу вгору .


1

Ключове запитання полягає в тому, що ви сказали в останньому до останнього абзацу: "Ви не можете так говорити в SQL". На цьому етапі вам може бути корисніше підходити до SQL як до іноземної, а не до мови програмування. Якщо ви думаєте про це таким чином, написання запиту SQL справді переводить англійське твердження про те, що ви хочете, у "SQLish". Комп'ютер прекрасно розуміє SQLish і зробить саме те, що ви говорите, тому вам не потрібно турбуватися про реалізацію, доки ви правильно перекладете.

Це означає, що найкращий спосіб вивчити іноземну мову? Ви, очевидно, повинні засвоїти граматику та лексику, які ви можете отримати зі своєї документації на SQL. Найголовніше - це практика. Ви повинні читати та писати стільки SQL, скільки можете, і не відчувати, що вам слід спочатку знати синтаксис; ви можете, і повинні, шукати речі, як ви йдете далі. Ви дізнаєтесь, що це отримаєте, коли вам буде легше описати, які дані ви бажаєте в SQL, ніж англійською.


1

Мені знадобилося багато часу, щоб також обернути голову навколо SQL. Ми зробили деяку теорію реляцій в університеті і в той час, яка служила лише для ускладнення справ. Врешті-решт мій навчальний процес був дуже пробним та помилковим, повідомлений різними навчальними матеріалами та прикладами, які я вважав корисними по дорозі. По суті, ви з часом звикнете до цього, і додавання нового способу мислення про дані та запити буде мати певне значення для вашого психічного розвитку.

Я виявив, що мені вдалося прискорити своє навчання шляхом поступового складання колекції простих сценаріїв, що демонструють, як використовувати кожну мовну функцію та як досягти певних результатів у відомій таблиці (визначення таблиці включені для довідок).

На початку цього року я проходив офіційну підготовку за проектом міграції даних у брудній базі даних Oracle, де мені доводилося поступово збирати фрагменти з моєї бібліотеки, щоб фільтрувати результати запитів різними способами, поки я не отримав саме те, що хотів, а потім перетворити їх як необхідні тощо. Деякі запити стали дуже складними та важкими для налагодження. Я сумніваюся, що я міг би їх прочитати зараз, але сподіваюся, що зможу знову дійти подібного рішення, використовуючи мої опорні будівельні блоки.

Іншими способами підвищення інтуїтивної обізнаності про декларативний та функціональний простори є вивчення теорії наборів та мов програмування, більше підходящих до певної парадигми. Зараз я перебуваю в процесі вивчення деяких Haskell, наприклад, для підтримки та вдосконалення своїх математичних здібностей.


0

Коли ви стикаєтеся з проблемою, ви зазвичай думаєте, як її вирішити. Але якщо ви знаєте, як комп'ютер це вирішує для вас! Тоді ви переймаєтесь тим, як буде ліквідовано.

Я намагаюся сказати, як це відбувається.

Можливо, ви вже знайомі з рекурсивними програмами, в рекурсивних програмах ви визначаєте проблему, а не кажете, як вона вирішена. Ви визначаєте базу і визначаєте n на основі n-1 . (наприклад factorial(n) = n * factorial(n-1)) Але ви вже можете знати, як комп'ютер це вирішує. він починається з функції і викликає функцію рекурсивно, поки не досягне базового визначення, а потім оцінює всі інші функції на основі базового значення.

Це те, що відбувається в декларативному програмуванні. ви визначаєте все, виходячи з існуючих визначень. І комп’ютер знає, як отримати відповідь за вас на основі основних функцій.

У SQL ви не можете співвідносити визначення один з одним, але ви пов'язуєте об'єкти чи інформацію один з одним, ви вказуєте, що ви хочете, і пошук за допомогою комп'ютера до чогось (об’єкта, інформації) на основі наданих вами відносин.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.