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


24

Я чувствую себя очень комфортно с императивным программированием. У меня никогда не возникает проблем с алгоритмическим выражением того, что я хочу, чтобы компьютер делал, как только я понял, что именно я хочу, чтобы он делал. Но когда дело доходит до таких языков, как SQL или я часто зацикливаюсь, потому что моя голова слишком привыкла к императивному программированию.

Например, предположим, что у вас есть группа отношений (bandName, bandCountry), venue (venueName, venueCountry), plays (bandName, venueName), и я хочу написать запрос: all venueNames, такой, что для каждого bandCountry есть группа из та страна, которая играет в месте с таким названием.

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

На мой взгляд, я сразу же иду «для каждого venueName итерации по всем bandCountries и для каждого bandCountry получаю список групп, которые из него приходят. Если ни один из них не играет в venueName, переходите к следующему venueName. Иначе, в конце bandCountries Итерация: добавьте venueName в набор хороших названий venue ".

... но вы не можете так говорить в SQL, и мне действительно нужно подумать о том, как это сформулировать, а интуитивное решение Imperative постоянно болтается в моей голове. У кого-нибудь еще была эта проблема? Как вы преодолели это? Вы выяснили смену парадигмы? Сделали карту из императивных понятий в понятия SQL для перевода императивных решений в декларативные? Читать хорошую книгу?

PS Я не ищу решение для вышеупомянутого запроса, я действительно решил это.


1
Это хороший вопрос, потому что вы озвучиваете слабость, которая есть у многих (включая меня).
Дэвид Вайзер

Может быть полезно определить, что вы подразумеваете под «отношением» в вашем вопросе. В реляционной модели (математика SQL) «отношение» примерно аналогично таблице SQL. Многие люди скажут «отношения», когда они действительно хотят сказать «отношения».
Джейсон Бейкер

Изучите теорию множеств и дискретную математику.

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

Ответы:


12

Идея декларативного действия заключается в том, что вы должны указывать что , а не как .

Для меня это звучит так, как будто вы на правильном пути. Проблема не в том, что вы думаете о вещах неправильно. Это то, что вы идете слишком далеко. Давайте посмотрим на то, что вы пытаетесь сделать:

Например, предположим, что у вас есть группа отношений (bandName, bandCountry), venue (venueName, venueCountry), plays (bandName, venueName), и я хочу написать запрос: all venueNames, такой, что для каждого bandCountry есть группа из та страна, которая играет в месте с таким названием.

Пока это здорово. Но тогда вы делаете это:

На мой взгляд, я сразу же иду «для каждого venueName итерации по всем bandCountries и для каждого bandCountry получаю список групп, которые из него приходят. Если ни один из них не играет в venueName, переходите к следующему venueName. Иначе, в конце bandCountries Итерация: добавьте venueName в набор хороших названий venue ".

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

На вашем месте я бы попробовал приобрести следующую привычку:

  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 можно считать более «элегантным» решением, чем временные переменные и подзапрос), но вполне очевидно, что мы ищем, поэтому мы оставим это для целей OP. ,

Цель ОП заключалась в том, чтобы научиться сдвигать образ мыслей с императивного на декларативный. Для этого посмотрите, что делает описанное императивное решение:

для каждого venueName итерируйте по всем bandCountries и для каждого bandCountry получите список бэндов, которые из него идут. Если никто из них не играет в venueName, перейдите к следующему названию. Иначе, в конце итерации bandCountries добавьте venueName к набору хороших названий.

Что является определяющим критерием в приведенном выше? Я думаю, что это:

... Если ни один из них [набор групп из определенной страны] не играет в venueName ...

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

Это хорошо для простых вещей, но помогает мыслить с точки зрения построения желаемого набора результатов; каковы соответствующие квалификационные критерии , которые позволили бы вместо этого заполнить ведро?

  • дисквалификатор: если нет группы из bandCountry, которая играет на месте, место дисквалифицируется
  • (частичный) квалификатор: если хотя бы одна группа из bandCountry играет на месте, то место может быть в порядке; продолжайте проверять остальную часть bandCountries
  • (полный) квалификатор: если хотя бы одна группа из каждой группы bandCountry играет в месте проведения, то место проведения квалифицируется

Окончательный классификатор может быть упрощен с помощью счетчиков: bandCountry «удовлетворен», если хотя бы одна группа оттуда играет в месте проведения; количество «удовлетворенных» стран группы для места проведения должно равняться количеству стран группы для места, которое будет квалифицировано.

Теперь мы можем рассуждать через отношения с помощью навигации:

  • начнем с отношения VENUE [оно нам не нужно для ответа, но это концептуальная отправная точка для реляционной навигации]
  • присоединиться к ИГРОКАМ на venueName
  • присоединиться к BAND на bandName, чтобы получить bandCountry
  • нас не волнует название группы; выберите только название места и bandCountry
  • нас не волнуют избыточные bandCountries; устранить дубликаты с помощью DISTRICT или GROUP BY
  • мы заботимся только о количестве отдельных стран, а не об именах
  • нам нужны только места, где количество отдельных bandCountries совпадает с общим количеством 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 в качестве первого шага в декларативном программировании может быть ошибкой, если вы действительно хотите изучить концепции. Конечно, исчисление кортежей, на котором оно основано, настолько декларативно, насколько это возможно, но, к сожалению, чистота исчисления кортежей была сильно нарушена реалиями реализации, и язык, по сути, стал немного запутанным беспорядком. Вместо этого вы можете захотеть взглянуть на другие более полезные (в том смысле, в каком вы привыкли) декларативные языки, такие как Лисп (особенно схема ), Haskell и ML для (в основном) функционального программирования или, альтернативно, Prolog и Mercury для (в основном) логическое программирование.

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

  1. Они полезны для программирования "от колыбели до могилы" - так как вы можете написать полную программу на этих языках от начала до конца. Они полезны сами по себе, в отличие от SQL, который на самом деле совершенно бесполезен для большинства людей как отдельный язык.

  2. Каждый из них дает вам разные взгляды на декларативное программирование, которое может дать вам разные пути к окончательному «получению».

  3. Они также дают вам разные взгляды на программирование в целом. Они улучшат вашу способность рассуждать о проблемах и кодировании, даже если вы никогда сами их не используете.

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

Я бы особенно рекомендовал изучать один из функциональных языков ( Clojure , как один из Лиспов, вероятно, является здесь хорошим выбором) и один из языков логики (мне больше всего нравится Mercury, но у Пролога есть гораздо больше полезного материала для изучения) для максимального расширения мыслительного процесса.


1

Нет ничего плохого в том, чтобы мыслить в декларативной среде, такой как SQL. Просто императивное мышление должно происходить на более высоком уровне, чем вы описали. Всякий раз, когда мне нужно запросить базу данных, которая использует SQL, я всегда думаю про себя:

  • Вот кусочки, которые мне нужны.
  • Я собираюсь соединить их таким образом.
  • Я собираюсь свести на нет то, что я только что получил, с помощью следующих предикатов, чтобы получить то, что я действительно ищу.

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


1

Ключ к вашему вопросу в том, что вы сказали в последнем абзаце: «Вы не можете так говорить в SQL». На данном этапе для вас может быть более полезным использовать SQL как иностранный язык вместо языка программирования. Если вы думаете об этом таким образом, написание SQL-запроса действительно переводит английскую формулировку того, что вы хотите, в «SQLish». Компьютер прекрасно понимает SQLish и будет делать именно то, что вы говорите, поэтому вам не нужно беспокоиться о реализации, если вы переводите правильно.

Тем не менее, каков лучший способ выучить иностранный язык? Очевидно, что вам нужно выучить грамматику и словарный запас, которые вы можете получить из документации по SQL. Самое главное это практика. Вы должны читать и писать столько SQL, сколько сможете, и не чувствуете, что сначала вам нужно тщательно знать синтаксис; вы можете и должны искать вещи по ходу дела. Вы поймете, что у вас это есть, когда вам будет проще описать, какие данные вам нужны в SQL, чем на английском.


1

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

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

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

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


0

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

Я пытаюсь сказать, как это происходит.

Возможно, вы уже знакомы с рекурсивными программами, в рекурсивных программах вы определяете проблему, а не говорите, как она решается. Вы определяете базу и определяете n на основе n-1 . (например factorial(n) = n * factorial(n-1)) Но вы уже можете знать, как компьютер решает эту проблему. он начинается с функции и вызывает функцию рекурсивно, пока не достигнет базового определения, а затем оценивает все другие функции на основе базового значения.

Это то, что происходит в декларативном программировании. Вы определяете все на основе существующих определений. И компьютер знает, как получить ответ для вас на основе основных функций.

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.