Декларативный язык

Я читал статью о декларативных языках программирования.

Если я не понимаю качества этого типа / парадигмы языков программирования и его отличие от императивных языков, должен ли я просто прочитать о программировании на языке программирования такого типа, как Haskell, а затем прочитать эту статью позже?

5 ответов

Смысл декларативной парадигмы - быть ленивым. Мы, декларативные программисты, хотим, чтобы компиляторы выполняли всю работу. Как можно чаще мы не указываем алгоритм, который мы хотим использовать, а только определение желаемых результатов.

Например, если в императивной установке вы хотите вычислить сумму целых чисел до n, вы могли бы написать (в C):

int f(int n) {
    int result = 0, i = 1;
    for(;i <= n; i ++)
        result += i;
    return result;
}

в декларативной настройке вы просто объявляете, что это за сумма (в Haskell):

f 0 = 0
f n = n + f (n - 1)

Это не алгоритм, это просто определение. Но компилятор Haskell достаточно умен, чтобы в любом случае получить нам результат.

Это еще более заметно, когда вы переключаетесь на Пролог, с каноническим примером, который следует:

  1. Мы заявляем о некоторых отношениях между разными людьми:

    father(luke, anakin).
    father(anakin, a_slave_on_tattouin).
    father(a_slave_on_tattouin, someone).
    father(someone, adam).
    
  2. Затем мы заявляем, что чей-то предок является его отцом или предком его отца:

    ancestor(Young, Old) :-
        father(Young, Old).
    
    ancestor(Young, VeryOld) :-
        father(Young, Old),
        ancestor(Old, VeryOld).
    

И вот, Пролог может раскрыть магию и ответить на такие вопросы, как:

?- ancestor(luke, X).
X = anakin ;
X = a_slave_on_tattouin ;
X = someone ;
X = adam ;
false.

Некоторые помогают читать эти вещи выше: Head :- Body средства Head если Body, Итак, выше

ancestor(Young, VeryOld) :-
    father(Young, Old),
    ancestor(Old, VeryOld).

средства VeryOld является Youngпредок если Old является Youngотец и VeryOld является Oldпредок.

А также ; означает или. Так вот, Пролог говорит нам, что у Луки 4 предка, anakin, a_slave_on_tattouin, так далее.

Как видите, мы ничего не указали в алгоритме, и все же Prolog может делать потрясающие вещи, такие как предоставление нам всех деталей lukeРодословная. Это сила декларативного программирования: вы указываете данные, это то, что вас волнует, а остальное - работа умных парней, работающих с компилятором.

Может потребоваться некоторое время, чтобы разобраться с Хаскеллом, Прологом и всем остальным, но это стоит сделать. Вот аналогия, чтобы вы начали с декларативной и императивной.

Представим, что вы хотите узнать, что такое 5sin(7x+20)+cos(x), когда x равен 5,279, и все, что у вас есть, - это ваш калькулятор. Если это старый калькулятор, вы должны сказать ему, как это сделать и в каком порядке. Если мы используем MC для чистой памяти до 0 и MR для воспоминания и памяти M+ для добавления текущего номера к тому, что в памяти, что вы на самом деле печатаете

MC       -- clear the memory
5.279    -- take the number 5.279
*7=      -- multiply it by seven
+20=     -- add twenty
sin=     -- find sin of that
*5=      -- times by five
M+       -- store that in the memory
5.279    -- take the number 5.279
cos      -- find cos of it
M+       -- add that to what you had in the memory
MR       -- get the total back out of memory onto the screen

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

Теперь любой язык программирования и любой современный калькулятор позволят вам печатать

5*sin(7*5.279+20)+cos(5.279)

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

5.279 -> x
5sin(7x+20)+cos(x)

Современные калькуляторы позволяют вам выразить проблему с точки зрения собственного понимания, а не разбивать ее на шаги для калькулятора. Некоторым людям нравится этот способ "просто скажи, что ты имеешь в виду", он находит это очень простым и не может понять, почему другие люди могут быть обеспокоены, делая это любым другим способом. Ясно. Вам не нужно объяснять калькулятору.

Хотя это 5.279 -> x; 5sin(7x+20)+cos(x) Это почти точно, как вы бы решили эту конкретную проблему на императивном языке программирования, мы сейчас говорим о стилях калькулятора, и первый способ сделать это со старым калькулятором является гораздо более императивным (шаг за шагом) в стиль, и это с новым калькулятором в гораздо более декларативном (просто скажи, что ты имеешь в виду) стиль.

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

take a piece of paper 7 by 10
pick up a red pen
put it at the point (3,4)   
draw a 2x4 rectangle here
colour it in
pick up a black pen
put it at the point (4,4)
draw a circle here radius 3
colour it in

тогда декларативный стиль "просто скажи, что ты имеешь в виду" будет

on a piece of paper 7 by 10
red 2x4 rectangle at (3,4)
underneath
black circle radius 3 at (4,4)

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

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

(Я говорил о доверии, потому что кто-то, кто читал черновик этого слова, сказал, что доверие было важным - она ​​объясняет, что использует калькулятор крайне важно, потому что она не доверяет ему, если она делает это декларативным способом. Я думаю, что это То же самое для программистов. Многие программисты не доверяют компиляторам настолько, чтобы быть декларативными - они хотят контроля и им самим нравится оптимизировать свой код.)

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

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

Язык, который обычно указывается в качестве примера декларативного программирования, - это Пролог (который на самом деле все еще находится в моем списке "для изучения"; SWI-Пролог доступен в репозиториях Debian, начиная с Squeeze).

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

Декларативное программирование в основном заключается в изобретении предметно-ориентированных языков (DSL) и программировании на этих подъязыках. Haskell - очень хороший выбор для этого стиля. В Haskell практически все доменные языки встроены в основной язык. Затем они называются встроенными DSL (EDSL). Другими словами, вам не нужно писать парсеры, но используйте элегантный легкий синтаксис Haskell. Позвольте мне назвать несколько примеров.

Допустим, вы хотите закодировать обработку изображения / видео. Вы замечаете, что почти все фильтры имеют что-то общее: они обновляют точки в зависимости от их окружения. Для этой конкретной цели существует абстрактное понятие, называемое комонады. Вы не выражаете изменения состояния, а вместо этого кодируете функцию перехода, связанную с фильтром. Другими словами, вы кодируете корреляции данных и зависимости. Обратите внимание на невероятную лаконичность фильтра размытия и тот факт, что он работает как для изображений, так и для видео (всего, что Filterable):

blur :: (Filterable img) => Radius -> img -> img
blur radius = extend (average . surroundingPoints radius)

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

text1 = textField "text1"
text2 = textField "text2"
sum   = fmap show (text1 + text2) <|> "Please enter numbers"

dialog =
    label "First number:" ~|~ text1
        ~---~
    label "Second number:" ~|~ text2
        ~---~
    label "Sum:" ~|~ label sum

Все, что необходимо для создания диалогового окна, включено в это описание, включая то, что происходит, когда пользователь не вводит цифры. Это не специальный язык и не псевдокод. Это настоящий код на Haskell (с использованием библиотеки Netwire)!

Другие вопросы по тегам