Публикация первой части ( habrahabr.ru/post/274603 ) вызвала довольно обширную и интересную дискуссию по различным аспектам языка применения ПРОЛОГ.
Цель была – показать опытным, и не очень, программистам, что ничего сложного в Прологе нет, и каждый может его применять в работе.
Почему-то не было вопросов непосредственно по тексту публикации. Буду думать, что там все понятно.
Приступим к рассмотрению более практических аспектов программирования на языке Пролог.
В этом разделе рассмотрим основные аспекты реализации на языке ПРОЛОГ трансляции контекстно-свободных языков на примере арифметических выражений.
Как известно, PROLOG возник из атрибутных грамматик. Поэтому синтаксический анализ – первое и естественное применение этого языка.
В отличие от такого средства, как регулярные выражения, на PROLOG легко писать трансляторы для более сложных — контекстно-свободных языков.
Для транслятора необходимо иметь лексический анализатор, синтаксический анализатор и генератор объектного кода.

1.Лексический анализ

Лексический анализатор на входе имеет строку символов, на выходе — список лексических единиц. Рассмотрим лексический анализатор для арифметических выражений.
Исходный предикат lexr/2 выполняет преобразование строки символов в список кодов символов, удаление пробелов (delb/2), с последующим переходом к lexr1/2, который рекурсивно просматривает входной список, выделяя из него лексические единицы — служебные символы и целые числа — последовательности цифр.
Предикат lexr1/2 завершает работу при исчерпании входного списка символов.
Предикат term/4 из входного списка символов выделяет очередную лексическую единицу Hs, которая записывается в начало выходного списка вызывающего предиката. Четвертый аргумент — оставшийся список, который будет обработан на следующем шаге рекурсии. Второй аргумент — служебный, для накопления списка цифр целого числа. В начале устанавливаем его в [].
Предикат term/4 состоит из четырех процедур для четырех ситуаций, которые могут возникнуть при просмотре входного списка.
Если в голове списка находится служебный символ, заносим его в выходной список, предварительно преобразовав его в строку. Это при условии, что рабочая переменная содержит пустой список.
Если же очередной элемент списка — служебный символ, но в рабочей переменной есть значения, накопленный список цифр из второго аргумента преобразуем в строку и заносим в выходной список. При этом служебный символ заносится в выходной список для повторного рассмотрения на следующем рекурсивном шаге.
Если очередной символ — цифра, то она заносится в конец второго аргумента — служебного списка и рекурсия продолжается.
Четвертая процедура включается при исчерпании входного списка при поиске очередной цифры числа. Исчерпание списка означает завершение просмотра целого числа.
Сервисные предикаты spec/1, digit/1 обеспечивают проверку кода символа на соответствие служебному символу или цифре.
Предикат member/2 выполняет проверку вхождения элемента в список.
Предикат append/3 выполняет соединение двух списков.
lexr(S,R):- list_text(L,S), delb(L,L1),lexr1(L1,R),!.

lexr1(L,[Hs|T1]):-
        term(L,[],Hs,L1),
        lexr1(L1,T1).
lexr1([],[]).

term([H|T],[],Hs,T):- spec(H),list_text([H],Hs).
term([H|T],L,I,[H|T]):- 
       L\=[],        
        spec(H),
        list_text(L,Ls),
        int_text(I,Ls).
term([H|T],L,R,Lr):- 
       digit(H),        
        append(L,[H],L1),
        term(T,L1,R,Lr).
term([],L,I,[]):-
        L\=[],
        list_text(L,Ls),
        int_text(I,Ls).

delb([32|T],L):-delb(T,L).
delb([H|T],[H|T1]):-delb(T,T1).
delb([],[]).

spec(H):-member(H,"+-*/()").
digit(H):- H>47, H<58.
append([H|T],L,[H|L2]):-append(T,L,L2).
append([],L,L).
member(H,[H|T]).
member(H,[H1|T]):-H\=H1,member(H,T).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%   
lr:-
        A = $(10-237*65+3/837467)-(342-678)$,
        lexr(A,L),
        write(A),nl,
        write(L),nl.

Надо отметить, что предикат lexr/2 реализует детерминированные вычисления — без бэктрекинга, поскольку лексический анализ не требует возвратов.
При возникновении бэктрекинга, он может выдавать неправильный результат.
Для исключения бэктрекинга, который может взывать внешняя процедура, в конце lexr/2 вставлена операция отсечения -"!", которая отключает возможность бэктрекинга предиката после его завершения.

2.DCG – грамматика

Все учебники по ПРОЛОГу пишут про DCG – Definite Clause Grammar и описывают это как магическое средство для построения синтаксического анализатора, с рассуждениями про разностные списки и декларативную семантику.
По моему мнению, DCG – просто удобный оператор для сокращения записи при описании грамматик.
ПРОЛОГ сам по себе так устроен, что в нем можно, прямо описав грамматику, получить синтаксический анализатор, точнее говоря, его основу.

Грамматика простого арифметического выражения.
Ex = :: Sterm | Sterm AdSign Ex
Sterm = :: AdSign Term | Term
Term =:: Factor | Factor MuSign Term1
Term1 = :: Term | Term MuSign Term1
Factor =:: ( Ex) | Number
AdSign =:: ‘+’ | ‘-‘
MuSign =:: ‘*’ | ‘/’

Можно переписать это непосредственно в виде предложений DCG.
ex -->  sterm. 
ex -->  sterm, adsign,ex.
sterm --> adsign, term.
sterm --> term.
term --> factor.
term --> factor,musign,term1.
term1 --> term.
term1 --> term, musign, term1.
factor --> [N],{number(N)}.
factor --> lb,ex,rb.
adsign --> ['+']. adsign --> ['-'].
musign --> ['*']. musign --> ['/'].
lb --> ['(']. rb --> [')'].


На вход надо подать список лексем.
e:-
A=[12,’-‘,98,’*’,’(‘,19,’+’,34,’*’,’(‘,200,’-‘,23,’)’,’)’, ],
ex(A,[]).

Если использовать лексический анализатор, входную строку можно ввести с устройства:

es:-
read_line(0,S),
lexr(S,L),
ex(L,[]).

Давайте посмотрим, как это работает. Есть такой встроенный предикат expand_term/2, который показывает, как система обрабатывает DCG – предложение.
Если набрать
expand_term((a--> b,c,d),L).
то получим:
L = a(A,B):-b(A,C),c(C,D),d(D,B)

Если набрать этот же предикат для терминального правила

expand_term((a--> [‘+’]),L).

то получим:
L= a([‘+’|T],T)

Терминальное правило хорошо иллюстрирует процессы, происходящие при выполнении грамматического разбора на ПРОЛОГе – каждое применение правила вычитает из входного списка нужные символы, удовлетворяющие этому правилу. В случае терминального символа, он должен находиться в голове текущего списка, «хвост» списка передается дальше.
При вызове первого, входного, правила грамматики, второй аргумент доложен быть пустым списком, чтобы была рассмотрена вся входная строка до конца.
Очевидно, что всякое правило в формате DCG легко переписать в виде основного типа предложений ПРОЛОГа – для нетерминальных правил надо добавить два аргумента в каждый вызов и заголовок, а для терминального правила – просто подставить константу в заголовок.
Отсюда ясно, что «магия» простоты реализации синтаксического анализа основана не на свойствах DCG – операторов, а на собственной природе языка ПРОЛОГ. DCG полезно тем, что освобождает от необходимости вставлять в каждый вызов две переменные и следить за правильным именованием входного и выходного списка каждого аргумента.
Конечно, пользы от такой программы немного, поскольку в качестве результата Вы получите только «Yes» или «No».
Получить синтаксическое дерево выражения также нетрудно – достаточно добавить соответствующие аргументы. Но в этом случае надо учесть приоритет операций.

3. Синтаксический анализ

Добавив один аргумент в каждое правило, получим на выходе синтаксическое дерево выражения. Здесь нам не понадобится прямая или обратная польская запись, поскольку синтаксическое дерево будет представлено непосредственно, в виде вложенных списков.
ex(R) -->  sterm(R). 
ex([S,R1,R2]) -->  sterm(R1), adsign(S),ex(R2).
sterm([S,R,[]]) --> adsign(S), term(R).
sterm(R) --> term(R).
term(R) --> factor(R).
term([S,R1,R2]) --> factor(R1),musign(S),term1(R2).
term1(R) --> term(R).
term1([S,R1,R2]) --> term(R1), musign(S), term1(R2).
factor(N) --> [N],{number(N)}.
factor(R) --> lb,ex(R),rb.
adsign('+') --> ['+']. adsign('-') --> ['-'].
musign('*') --> ['*']. musign('/') --> ['/'].
lb --> ['(']. rb --> [')'].

Для вызова такой программы надо указывать три параметра
e3:-
        S='10-3-5+4',
        lexr(S,L),
        ex(R,L,[]),
        calc(R,Res),
        write(S),nl,
        write(R),nl,
        write(Res),nl.

Результат синтаксического анализа в приведенном примере будет иметь вид:

[-,10,[-,3,[+,5,4]]]

Рекурсивный предикат calc/2 выполняет вычисление значения арифметического выражения по его синтаксическому дереву.
calc([S,A1,A2],Nr):-calc(A1,N1),calc(A2,N2),calc1(S,N1,N2,Nr),!.
calc(A1,A1):-A1\=[_|_].
calc1(*,N1,N2,Nr):- Nr is N1*N2.
calc1(/,N1,N2,Nr):- Nr is N1/N2.
calc1(+,N1,N2,Nr):- Nr is N1+N2.
calc1(-,N1,N2,Nr):- Nr is N1-N2.

Для данного примера результат будет – «16». Однако он оказывается неверным, должно быть «6»! Действительно – синтаксическое дерево построено неправильно – оно соответствует выполнению операций справа налево. Поскольку арифметические операции левоассоциативны, получен неверный результат.
Грамматика, которая годилась для проверки правильности арифметического выражения, была построена без учета ассоциативности операций.
Правило
ex([S,R1,R2]) --> sterm(R1), adsign(S),ex1(R2).

надо заменить на правило вида

ex([S,R1,R2]) --> ex(R1), adsign(S),term(R2).

Однако такое правило содержит левую рекурсию и будет зацикливаться. Что делать? Выход из положения такой – «раздробить» рекурсивный вызов на составные части, тогда уже рекурсия не будет левой – входной аргумент изменится!
ex(E)-->eterm(E).
ex([S,E1,E2])-->sterm(E1),sn(S),eterm(E2).

sterm(E)-->eterm(E).
sterm([S,E1,E2])-->eterm(E1),sn(S),eterm(E2).
sterm([S2,[S1,E1,E2],E3])--> eterm(E1),sn(S1),sterm(E2),sn(S2),eterm(E3).

eterm(E)-->fct(E).
eterm([S2,[S1,E1,E2],E3])--> fct(E1),sn2(S1),eterm(E2),sn2(S2),fct(E3).
eterm([S,E1,E2])-->fct(E1),sn2(S),fct(E2).
sn2(*)-->[*]. sn2(/)-->[/]. sn2(div)-->[div]. sn2(mod)-->[mod]. sn2(and)-->[and]. 
fct(E)-->number(E).
fct(E)-->lb,ex(E),rb.
fct(E)-->snot,fct(E).
number(X)-->[X],{number(X)}.
lb-->['('].
rb-->[')'].
sg(+)-->[+].
sg(-)-->[-].
sn(E)-->sg(E).

Для данной грамматики синтаксическое дерево нашего примера будет иметь вид

[+,[-,[-,10,3],5],4]

а полученное значение будет равно «6».

4.Непосредственное вычисление

Синтаксическое дерево разбора выражения может быть использовано для генерации объектного кода путем рекурсивного просмотра процедурой, аналогичной вышеприведенной calc/2.
В тех случаях, когда требуется только вычисление арифметического выражения, можно не строить дерево, а выполнять вычисление в ходе синтаксического анализа.
ex(E)-->eterm(E).
ex(R)-->sterm(E1),sn(S),eterm(E2),{clc([S,E1,E2],R)}.

sterm(E)-->eterm(E).
sterm(R)-->eterm(E1),sn(S),eterm(E2),{clc([S,E1,E2],R)}.
sterm(R)-->eterm(E1),sn(S1),sterm(E2),sn(S2),eterm(E3),{clc([S1,E1,E2],N),
        clc([S2,N,E3],R)}.

eterm(E)-->fct(E).
eterm(R)-->fct(E1),sn2(S1),eterm(E2),sn2(S2),fct(E3),{clc([S1,E1,E2],N),
        clc([S2,N,E3],R)}.
eterm(R)-->fct(E1),sn2(S),fct(E2),{clc([S,E1,E2],R)}.
sn2(*)-->[*]. sn2(/)-->[/]. sn2(div)-->[div]. sn2(mod)-->[mod]. sn2(and)-->[and]. 
fct(E)-->number(E).
fct(E)-->lb,ex(E),rb.
number(X)-->[X],{number(X)}.
lb-->['('].
rb-->[')'].
sg(+)-->[+].
sg(-)-->[-].
sn(E)-->sg(E).
clc(A1,A1):-A1\=[_|_].
clc([*,N1,N2],Nr):- Nr is N1*N2.
clc([/,N1,N2],Nr):- Nr is N1/N2.
clc([+,N1,N2],Nr):- Nr is N1+N2.
clc([-,N1,N2],Nr):- Nr is N1-N2.   

В приведенной программе можно заметить фигурные скобки. Такие скобки используются в случае применения «обычных» предикатов ПРОЛОГа, т.е. для них не надо выполнять добавление двух аргументов.
На основе такого подхода можно строить трансляторы различных контекстно-свободных языков, в том числе, HTML, XML.

В третьей части рассмотрим применение Пролога для построения одного типа интеллектуальных систем — экспертных систем.

Комментарии (150)


  1. norguhtar
    11.01.2016 11:08

    У пролога есть одна довольно значимая проблема. Сделать на нем какой либо интерфейс взаимодействия с пользователем тот еще цирк. Как итог его неплохо бы использовать на манер lua т.е. исключительно как встраиваемый язык. Я с ним имел дело довольно таки давно, как с этим сейчас?


    1. N_Ikhsanov
      11.01.2016 12:39

      В разных реализациях языка есть свои средства — встроенные предикаты для взаимодействия с внешней средой.
      Можно сделать и по другому — сделать на ПРОЛОГе бbблиотеку или DLL.


      1. norguhtar
        11.01.2016 12:57

        Можно сделать и по другому — сделать на ПРОЛОГе бbблиотеку или DLL.

        Опять же получается в лыжах на асфальте. В этом и проблема. А на чистом прологе писать ПО мягко говоря не очень. Это кстати в свое время сильно ограничивало использование как его так и лиспа.


        1. N_Ikhsanov
          11.01.2016 13:31

          ПРОЛОГ — язык специализированный — для задач обработки символьной информации и искусственного интеллекта.


          1. norguhtar
            11.01.2016 13:31

            Иии? Что толку от него если я им не могу воспользоваться, а средства ввода-вывода убоги до нельзя?


            1. N_Ikhsanov
              11.01.2016 14:18
              +1

              Разве Вы в своей работе пользуетесь только одним языком программирования?


              1. norguhtar
                11.01.2016 14:44

                Внимательно читаем первый коммент. Я указываю проблему Prolog это интеграция. Из-за этого его использование в том числе в полезных целях весьма затруднено.


                1. N_Ikhsanov
                  11.01.2016 14:48
                  +1

                  Может быть Вы не смотрели профессиональный ПРОЛОГ? Не вижу проблем с интеграцией.


                  1. norguhtar
                    11.01.2016 15:25
                    -1

                    Эм, а что такое профессиональный ПРОЛОГ? :)


                    1. N_Ikhsanov
                      11.01.2016 18:44
                      +1

                      Который продается, например SICStus Prolog.


                1. FiresShadow
                  11.01.2016 14:59

                  Я указываю проблему Prolog это интеграция.

                  Не занимался интеграцией с прологом, но первое, что приходит в голову — это передача итоговых данных по TCP на localhost. И это не единственный способ.


                  1. N_Ikhsanov
                    11.01.2016 15:04

                    Во всех реализациях ПРОЛОГа большое внимание уделяется средствам интеграции, есть несколько вариантов.
                    Например, в ARITY/PROLOG есть просто встроенный Си, не говоря уже о том, что можно компилировать ПРОЛОГ в obj — файл, DLL или EXE.


                    1. norguhtar
                      11.01.2016 15:27

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


                      1. N_Ikhsanov
                        11.01.2016 18:45
                        +1

                        На Прологе делаете DLL и вызываете из своей «обычной» программы.


                  1. norguhtar
                    11.01.2016 15:28

                    Ну опять же tcp для пролога тоже не очень нативный метод :)


                    1. N_Ikhsanov
                      11.01.2016 18:47

                      Если хочется, можно на ПРОЛОГе сделать EXE и вызывать его через средства управления процессами.


            1. Danov
              11.01.2016 18:53
              +1

              Метафорой отвечу: А как на языке мат алгебры можно ввод вывод писать???
              Языки предназначены для описания ограниченной предметной области.


    1. FiresShadow
      11.01.2016 14:54

      Сделать на нем какой либо интерфейс взаимодействия с пользователем тот еще цирк.

      Есть Visual Prolog. Например, button::defaultAction :- Name = idc_ancestordialog_personname:getText(). В логической парадигме программирования нет присвоения переменных и вызовов функций, поэтому концептуально это можно перевести как «defaultAction истинно, когда Name совпадает с тем, что ввёл пользователь в окошко». Потом в каком-то месте программы утверждается, что defaultAction истинно (напомню, вызовов функций нет), и Prolog начинает искать все Name, которые совпадают с тем, что ввёл пользователь. Для этого у пользователя просят ввести данные. После этого считается, что Name определён. В общем-то да, GUI в логической парадигме немного запутаннее, чем в функциональной или объектно-ориентированной, но тем не менее, вполне осуществим.


      1. norguhtar
        11.01.2016 15:25

        Как итог имеем отдельную прослойку которая по сути императивна :) Я по этому и говорю, что более правильно prolog бы в виде встраиваемого языка использовать аля lua. В этом случае такой проблемы как работа с GUI нет :)


        1. FiresShadow
          11.01.2016 17:17
          +1

          Ну да, пролог не для GUI. В программе, написанной на языке логического программирования, решение задачи часто ищется посредством хорошо оптимизированного брутфорса. Изначально это задумывалось как перебор математических фактов с целью доказательства теорем. Можно создать обычную прикладную программу на прологе, и она будет гораздо гибче и понятнее, чем программа из любой другой парадигмы, но будет она очень тормознутой — ибо брутфорс. Можно написать на прологе и быструю программу, но тогда нужно будет не просто описывать известные факты о системе, а как бы направлять ход выполнения программы, по сути возвращаясь к императивному программированию. Логическое программирование тем и ценно, что программист, например, описывает что такое ток, проводочки и диоды, задаёт максимальное количество элементов на микросхеме, и просит программу собрать ему синхрофазотрон. Без шуток, этот очень оптимизированный брутфорс творит чудеса, и Ватсон свидетель чудес его!


  1. KvanTTT
    11.01.2016 11:23
    +1

    Вот вы пишете про лексический и синтаксический анализ на прологе. В чем его преимущества по сравнению с тем же ANTLR, особенно с учетом такого не слишком понятного синтаксиса?


    1. N_Ikhsanov
      11.01.2016 12:45
      -2

      Для понимания синтаксиса написана ч.1. ( habrahabr.ru/post/274603 )
      Существует множество всяких систем построения синтаксических анализаторов — сравнивать со всеми не имею возможности.
      Цель была — показать естественность операций, связанных с парсингом, в ПРОЛОГе, поскольку грамматика непосредственно отображается в язык.


      1. begemot_sun
        11.01.2016 14:40

        В таком случае «естественный» парсинг подойдет и для Erlang и для Haskell. Там это будет более естественное, т.к. там тоже есть паттерн-матчинг.

        Так какова же киллер-фича для построения лексеров/парсеров? мне кажется в разрезе Erlang/Haskell на Prolog это делается сложнее.


        1. N_Ikhsanov
          11.01.2016 14:45

          В ПРОЛОГе — это DCG.
          А Вы не могли бы уточнить, почему «в разрезе Erlang/Haskell на Prolog это делается сложнее.»?


  1. lair
    11.01.2016 12:11

    Вы правда считаете это понятным языком?

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


    1. begemot_sun
      11.01.2016 12:34
      +1

      согласен, все примеры можно было бы прогнать через highlighter


    1. N_Ikhsanov
      11.01.2016 12:46

      См ч.1


      1. lair
        11.01.2016 12:47

        Там нет ответа на этот вопрос, либо я его не нашел. Можете привести цитату?


        1. N_Ikhsanov
          11.01.2016 13:29

          Цель первой части — описать Пролог с процедурной точки зрения, чтобы нетрудно было понять этот язык «обычному» программисту.


          1. lair
            11.01.2016 13:35

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


            1. N_Ikhsanov
              11.01.2016 14:02
              +2

              Я считаю, да — является понятным языком. А Вы не можете прочесть (а не просто просмотреть) ч.1 и сказать свое впечатление по понятности описания языка?


              1. lair
                11.01.2016 14:18

                Я же так и сделал. Код в этой (второй) статье понятнее не становится. Повторюсь, дело может быть не в языке, а конкретно в ваших примерах.


                1. N_Ikhsanov
                  11.01.2016 14:19

                  А Вы не можете задать конкретный вопрос — что именно непонятно?


                  1. lair
                    11.01.2016 14:48

                    Да далеко не ходить: lexr(S,R):- list_text(L,S), delb(L,L1),lexr1(L1,R),!.

                    Из этой строчки понятно, что предикат lexr/2 последовательно вызывает три предиката: list_text/2, delb/2, lexr1/2. Это все, что из нее понятно. А еще в ней, на самом деле, происходит последовательное присвоение (не уверен, что это правильный термин, правда) результатов предикатов для их использования в следующем предикате.

                    Интереса ради можно сравнить:

                    let lexer input =
                      let rec lexer input = 
                        //очень грубый пример, просто чтобы показать матчинг и рекурсию
                        match input with
                        | [] -> []
                        | [h; t] -> [term(h); lexer(t)]
                      
                      input
                      |> String.ToCharArray
                      |> Seq.filter (fun c -> c != ' ')
                      |> lexer
                    


                    1. lair
                      11.01.2016 14:54

                      Сорри, задумался. Не [term(h); lexer(t)], а term(h) :: lexer(t)


                    1. N_Ikhsanov
                      11.01.2016 14:56

                      lexr(S,R):- list_text(L,S), delb(L,L1),lexr1(L1,R),!..

                      list_text(L,S) — встроенный предикат, преобразует строку всписок кодов символов.
                      delb(L,L1) — удаление пробелов, описан ниже.
                      lexr1(L1,R) — основной предикат лексического анализа — описан ниже по тексту.
                      У Вас примерно такой же код по объему, только Вам он привычен.


                      1. lair
                        11.01.2016 14:58

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


                        1. N_Ikhsanov
                          11.01.2016 15:06

                          Не знаю, что Вам ответить. Мне кажется, мы с Вами впадаем в холивар или по-русски — в перепалку.


                        1. MacIn
                          11.01.2016 18:00
                          +1

                          Код понятен. То, что предикаты названы в стиле a1v25b9 — не вина языка.


                          1. lair
                            11.01.2016 18:03

                            Я про это с самого начала пишу.


                            1. N_Ikhsanov
                              11.01.2016 18:51
                              +1

                              Конечно, можно написать длинные и красивые имена предикатов — ограничений нет. Мне так не хочется делать, потому что имена предикатов в ПРОЛОГе повторяются чаще, чем в обычных языках.


                    1. FiresShadow
                      11.01.2016 15:09

                      предикат lexr/2 последовательно вызывает три предиката: list_text/2, delb/2, lexr1/2.

                      Эта фраза не совсем корректна, в логической парадигме нет понятия «вызов функции» или вызова чего бы то ни было. Точно также нет присвоения переменных.
                      Согласен, имена предикатов в данном случае малоинформативные.


                      1. lair
                        11.01.2016 15:39

                        Эта фраза не совсем корректна, в логической парадигме нет понятия «вызов функции» или вызова чего бы то ни было. Точно также нет присвоения переменных.

                        Да, я понимаю это, но опыта правильно это выразить мне не хватает.

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


                        1. N_Ikhsanov
                          11.01.2016 15:45

                          Давайте сделаем так — Вы напишете эквивалентные программы на HASKELL, потом сравним.


                          1. lair
                            11.01.2016 15:47

                            А почему именно на Хаскеле, которого я еще и не знаю?

                            (впрочем, пустой вопрос, я не уверен, что есть смысл это на любом языке делать: у вас выше спросили про разницу с ANTLR, вы от ответа ушли)


                            1. N_Ikhsanov
                              11.01.2016 15:52

                              Разве Ваши примеры:
                              let lexer input =
                              let rec lexer input =
                              //очень грубый пример, просто чтобы показать матчинг и рекурсию
                              match input with
                              | [] -> []
                              | [h; t] -> [term(h); lexer(t)]
                              не на этом языке приводятся?
                              Если это другой язык — можно на нем написать.


                              1. mird
                                11.01.2016 16:13

                                Это f#


                              1. lair
                                11.01.2016 16:15

                                не на этом языке приводятся?

                                Нет, не на этом.

                                Если это другой язык — можно на нем написать.

                                На самом деле, весь ваш «лексер» сводится к:

                                let lexer =
                                  String.toCharArray
                                  >> Seq.filter (fun c -> c != ' ')
                                  >> Rx.window (fun c -> c |> Char.IsDigit |> not)
                                


                                Где Rx.window — это вполне типовая операция над последовательностями, которая превращает последовательность в последовательность последовательностей, разделяемых по условию.


                                1. N_Ikhsanov
                                  11.01.2016 18:57

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


                                  1. lair
                                    11.01.2016 19:05
                                    +1

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


                                    1. N_Ikhsanov
                                      11.01.2016 20:06

                                      Что тут обсуждать? ПРОЛОГ — полиморфный язык. Если очень хочется, в основной части языка есть стандартная операция call X, где X — переменная, которой можно динамически присваивать значение — любой предикат, точнее, цель.
                                      Я Вам стремлюсь показать природу языка, а ВЫ говорите о фичах. Пишите фичи сколько хотите, потом еще можете сказать, что у Вы создали новый язык программирования, лучше ПРОЛОГа.


                                      1. lair
                                        11.01.2016 21:01
                                        +3

                                        Я Вам стремлюсь показать природу языка

                                        Пока что вам это плохо удается. FiresShadow это удалось намного лучше.

                                        а ВЫ говорите о фичах

                                        Я не знаю, что вы понимаете под словом «фича», но, похоже, совсем не то, что я.


                                        1. N_Ikhsanov
                                          11.01.2016 22:50

                                          firesshadow дал хорошее описание в стиле «смотри как бывает!». Моя цель — «смотри как ты можешь сделать!»
                                          А под словом «фича» я понимаю специализированные процедуры, встраиваемые в язык.


                                          1. lair
                                            11.01.2016 22:56
                                            +2

                                            Моя цель — «смотри как ты можешь сделать!»

                                            … как я уже говорил, ваше «смотри» показывает мне вещи, которые я могу сделать на другом языке как минимум проще, и, скорее всего, лучше.

                                            А под словом «фича» я понимаю специализированные процедуры, встраиваемые в язык.

                                            Вот только в моем примере не было ничего, подходящего под это определение.


                                    1. N_Ikhsanov
                                      11.01.2016 20:09

                                      У меня еще возник вопрос — интерес к символьным языкам у Вас чисто академический или он связан с работой? Если Вы практически такими задачами заняты, может лучше Ваши задачи обсудить, точнее, способы их решения.


                                      1. lair
                                        11.01.2016 21:02

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


                                        1. N_Ikhsanov
                                          11.01.2016 22:54

                                          Язык F#, как я понял по синтаксису Вашего примера — микрософтовская реализация языка HASKELL.
                                          Почему-то многие, встречая что-то новое, включают защитный рефлекс.
                                          Мне хотелось просто показать естественность и простоту там, где обычно читателя смущают новой непонятной терминологией.


                                          1. lair
                                            11.01.2016 22:58

                                            Язык F#, как я понял по синтаксису Вашего примера — микрософтовская реализация языка HASKELL.

                                            Вы поняли неправильно.

                                            Мне хотелось просто показать естественность и простоту там,

                                            И снова, вам это не удалось. Ваши примеры не выглядят ни естественными, ни простыми.


                                          1. Vitter
                                            20.01.2016 02:24

                                            F# это «реализация» OCaml в .NET


                            1. N_Ikhsanov
                              11.01.2016 22:57

                              Что я могу ответить, если не знаком с этой системой? Да и какая разница — ведь таких систем генерации полезных программ множество.


                              1. lair
                                11.01.2016 23:00

                                Что я могу ответить, если не знаком с этой системой?

                                Сравните с другими современными распространенными системами парсинга, не суть. Просто без такого сравнения — как вы можете говорить, что пролог чем-то лучше?


                                1. N_Ikhsanov
                                  12.01.2016 07:46

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


                                  1. lair
                                    12.01.2016 11:54
                                    +1

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

                                    И чем это отличается от Пролога? Тем, что «параметры» описываются в DCG?

                                    Понимаете, это все — инструменты. У меня есть информационная система, типичный LOB, ввод данных и отчетики. И мне надо добавить в нее построение отчетов на основе SQL-подобного языка. Соответственно, мне нужно распарсить входную строку до AST. Я могу взять пролог и встроить его к себе, а могу взять antlr/lex+yacc/younameit и встроить их к себе. Для решения этой задачи пролог — всего лишь один из инструментов, совершенно одинаковый в ряду прочих, и их сравнение полностью оправдано.

                                    Суть, природу языка демонстрируем

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


                                    1. N_Ikhsanov
                                      12.01.2016 15:25

                                      Может Вам это и не надо — у Вас есть привычный набор инструментов. Предложите что-нибудь конструктивное.


                                      1. lair
                                        12.01.2016 15:33

                                        А уже предложили выше: расскажите, в чем преимущества пролога для такой задачи по сравнению с другими инструментами.


                                        1. N_Ikhsanov
                                          12.01.2016 15:34

                                          Изучать другие инструменты мне?


                                          1. lair
                                            12.01.2016 15:35

                                            Конечно, вам. Вы же зачем-то утверждаете, что пролог для этой задачи хорошо подходит.


                                    1. N_Ikhsanov
                                      12.01.2016 15:29

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


                                      1. lair
                                        12.01.2016 15:33

                                        Уже подумали, отсюда и возникла задача «построение отчетов на основе SQL-подобного языка». Выражение для построения отчета пишет пользователь.


                                        1. N_Ikhsanov
                                          12.01.2016 15:38

                                          А пользователь должен его выучить? И консультироваться с программистами? А если сделать так, чтобы пользователю не требовались консультации и обучение?


                                          1. lair
                                            12.01.2016 15:40

                                            А пользователь должен его выучить?

                                            Да.

                                            А если сделать так, чтобы пользователю не требовались консультации и обучение?

                                            В общем случае это невозможно — по крайней мере, в разумной перспективе развития этой системы (3-5 лет).


                                            1. N_Ikhsanov
                                              12.01.2016 15:42

                                              Экспертные системы давно придумали.


                                              1. lair
                                                12.01.2016 15:43

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


                                                1. N_Ikhsanov
                                                  12.01.2016 15:53

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


                                                  1. lair
                                                    12.01.2016 15:54

                                                    Ну вот видите, а вы говорите — давно придумали. Придумать-то придумали, да исказили, потому и взять прямо сейчас нечего.


                                                    1. N_Ikhsanov
                                                      12.01.2016 15:58

                                                      Вы хотите готовый инструмент. А что-то подобное самому сочинить?


                                                      1. lair
                                                        12.01.2016 16:01

                                                        Я предпочитаю тратить свое свободное время на что-нибудь другое. А бюджет в проекте на это, предсказуемо, никто не даст.


                                                      1. rg_software
                                                        12.01.2016 20:52

                                                        Мне кажется, вы неявно предполагаете, что основная проблема пользователя — это выучить SQL-подобный (или любой другой) язык программировния, чтобы генерировать отчёты и тому подобное. Это ведь тоже довольно старая мысль: давайте вместо Pascal/Java/etc. дадим пользователю нечто вроде русского языка или вообще графические блоки, он будет соединять их стрелочками, и наступит всеобщее счастье.

                                                        Да вот практика показывает, что основная проблема не в языке, а в программистском способе мышления, которым необходимо овладеть, чтобы рисовать правильные стрелочки, так что всё равно определённый тренинг здесь необходим.

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


                                                        1. N_Ikhsanov
                                                          15.01.2016 15:42
                                                          -1

                                                          Основная проблема программистов — в алгоритмическом мышлении. Почему-то они думают, что пользователь должен мыслить алгоритмически. Нет, конечный пользователь знает свою работу и не должен быть каким-то слабым программистом на упрощенном языке интерфейса, который ему дает программист. Пользователь должен описать свою проблему, а вот превратить ее в программу — задача программиста. Пользователь должен описывать ЧТО ему требуется, а программисты дают ему такие средства, что пользователь должен дополнительно учиться описывать не свою задачу, а способ ее решения, последовательность действий через интерфейс, предоставленный ему программистом.


                                                          1. MacIn
                                                            15.01.2016 18:00

                                                            Между пользователем и собственно программистом вы забываете постановщиков задачи, проектировщиков, архитекторов. Это их дело — выяснить, что пользователю нужно, и переложить в ТЗ для программиста.


                                                          1. rg_software
                                                            15.01.2016 23:26
                                                            +2

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

                                                            Вы по сути повторяете вот эту старую мысль: проблема именно в сложности языков вроде Java или Pascal или C, а вот если бы пользователю дали писать на условном русском языке или рисовать диаграммы, то сразу бы рожь заколосилась и наступило бы всеобщее счастье.

                                                            В реальности (по крайней мере, так я понимаю из литературы и личного опыта) язык — это уже самый распоследний бастион. Человек неподготовленный в принципе мыслит неформально. Он может сказать, например, что ему нужно определить существование заданной карточки вида «Имя Фамилия» в базе данных. А когда даёшь ему то, что требовалось, оказывается, что «Нефёдов» и «Нефедов» нужно рассматривать как одно и то же, или что одинаковы строки «Иван Иванов» и «Иван <четыре пробела> Иванов», или «Иван» и «иван» — короче говоря, масса тонкостей, о которых люди не думают.

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


                        1. MacIn
                          11.01.2016 18:03

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

                          Просто потому что автор избрал не ту тактику :) Он вместо того, чтобы показать задачи, под кооторые Пролог «заточен», показывает решение привычных в императивной парадигме вещей. Но посколькку они императивны, то в Прологе выглядят плохо. Только и всего.


                          1. lair
                            11.01.2016 18:04

                            Тут немедленно возникает разумный вопрос: под какие же задачи пролог заточен?


                            1. MacIn
                              11.01.2016 18:06
                              +2

                              На мой взгляд — проверка набора фактов. Это его основа. Вы можете сделать это императивно, но на Прологе это выглядит еще и понятно.


                            1. FiresShadow
                              11.01.2016 18:26
                              +1

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


                          1. N_Ikhsanov
                            11.01.2016 19:01

                            В ПРОЛОГе точно также можно заготовить всякие типовые процедуры, а затем вызывать их в две строки.
                            ПРОЛОГ предназначен для работы с символьной информацией и для решения задач, требующих логического вывода.
                            Заготовить процедуры на все случаи жизни — это делает любой программист и не надо называть это «заточенностью.»


                            1. MacIn
                              11.01.2016 20:41
                              +1

                              Заготовленные процедуры здесь ни при чем. Пролог позволяет нам проверить набор фактов — это его задача, для этого язык создан. То, что мы можем, используя императивную сущность пролог-машины и отсечение, делать императивные вещи — отдельный разговор.
                              Вы можете написать на html/css (чисто декларативном языке) игру, которая будет интерактивной. Потому что браузер, интерпретирующий html и css, императивен внутри. Это не отменяет того, что html — язык разметки, он под это «заточен».


                    1. MacIn
                      11.01.2016 18:01

                      А еще в ней, на самом деле, происходит последовательное присвоение (не уверен, что это правильный термин, правда) результатов предикатов для их использования в следующем предикате

                      Это издержки императивности реализации. Правильный термин — подстановка ЕМНИП.


                      1. N_Ikhsanov
                        11.01.2016 20:11

                        Правильный термин — унификация, которая по своей природе является подстановкой аналогично тому как подставляются ссылочные фактические параметры при вызове процедуры.


                        1. MacIn
                          11.01.2016 20:29

                          Да, точно. Унификация.


          1. retran
            11.01.2016 15:30
            +1

            Зачем описывать Пролог с процедурной точки зрения, если у него совершенно другая вычислительная модель и он решает совершенно другие задачи? Тем более, что сам язык объективно мертв по очень простой причине — реализовать прологовскую модель вычислений (а точнее целое семейство алгоритмов родившихся из нее) под конкретную задачу на любом современном императивном/функциональном проще чем пытаться родить на прологе то для чего он не предназначен.


            1. N_Ikhsanov
              11.01.2016 15:48

              Во всех учебниках ПРОЛОГ описывается с двух точек зрения — декларативной парадигмы и процедурной.
              Но декларативная (или логическая) семантика не полностью реализована в языке и только затуманивает понимание языка, если мы хотим на нем делать реальные программы.


              1. retran
                11.01.2016 15:55
                +2

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


                1. N_Ikhsanov
                  11.01.2016 19:06
                  -1

                  Если куски ПРОЛОГа вставляют в эти необъятные монстры — нагромождения бесконечного количества библиотек, называемые современными языками программирования, то чем тут гордиться?
                  Вам показывают суть парадигмы логического программирования, а стандартные процедуры есть в каждой реализации — можете на них посмотреть, если хочется.


                  1. retran
                    12.01.2016 13:35

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

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


                    1. N_Ikhsanov
                      12.01.2016 15:33

                      Синтаксический анализ — функциональная задача?


                      1. retran
                        12.01.2016 15:51
                        +1

                        Если понимать под синтаксическим анализом рекурсивный обход дерева — то да. Заставлять пролог выполнять тот же обход через решение графа отношений — не очень хорошая идея.


                        1. N_Ikhsanov
                          12.01.2016 15:56

                          Разве ПРОЛОГ делает это неэффективно? Там простой перебор вариантов.


                          1. retran
                            12.01.2016 16:01

                            Вопрос не в эффективности, а в понятности, наглядности и той самой декларативности кода.


                          1. lair
                            12.01.2016 16:05

                            Вообще, «простой перебор вариантов» — далеко не всегда самый эффективный способ решения задачи.


              1. rg_software
                12.01.2016 01:28

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

                И я как раз соглашусь с новым комментарием — в том и беда, что декларативность в Прологе только декларируется (каламбур-с), а на самом деле приходится всё равно размышлять над тем, как машинка под капотом все эти факты и правила будет обрабатывать. То есть скрытая императивность.

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


                1. MacIn
                  12.01.2016 02:24

                  То есть скрытая императивность.

                  В случае, если мы пишем что-то императивное и пользуемся императивностью самой Пролог-машины. Т.е. используем инструмент в чужой среде.


                  1. rg_software
                    12.01.2016 05:29
                    +1

                    Я немного о другом. Декларативность — это когда (как нам пытаются объяснить книжки по декларативному программированию) мы описываем какие-то соотношения между элементами предметной области, а компьютер «сам» выводит ответы на интересующие нас запросы.

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

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


                    1. MacIn
                      12.01.2016 14:59

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

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


                      1. rg_software
                        12.01.2016 20:31
                        +2

                        Нет, я всё-таки о другом. Мне абсолютно всё равно, какое оборудование выполняет мою программу, речь именно о том, что мне как программисту приходится об этом думать.

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


                        1. MacIn
                          12.01.2016 22:52

                          речь именно о том, что мне как программисту приходится об этом думать.

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


                      1. michael_vostrikov
                        13.01.2016 07:32

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

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


                        1. MacIn
                          13.01.2016 11:27

                          А дождь падает с неба на землю. К чему трюизмы?
                          Про распространение сигнала — вы немного уходите не в ту степь. Да, задержка есть, но это не сравнимо с итеративным подходом. Для выполнения двух действий на итеративной машине мы всегда сначала выполним одно, затем другое. То, что они могут занимать разное время — про это я не говорю. В аналоговой системе если вы подключите к источнику сигнала 2 одинаковых блока (не реальных, а идеалистичных), то сигнал до них дойдет одновременно и обрабатывать они его будут одновременно. Это не отменяет задержек, но я по-русски написал «условно». Можно было говорить о многоядерной системе и параллельных вычислениях, или просто комбинационных схемах.


                  1. N_Ikhsanov
                    12.01.2016 07:40

                    Зачем эти спекуляции на тему декларативности? Если Вы внимательно прочитали текст — моя цель показать две вещи:
                    — в ПРОЛОГе нет ничего сверхсложного и всякий программист может его применять:
                    — есть задачи задачи, которые на ПРОЛОГе делать лучше, чем в императивном языке.


                    1. MacIn
                      12.01.2016 14:57

                      Это не про вашу статью сказано, не заводитесь.


                1. N_Ikhsanov
                  12.01.2016 07:34

                  Я написал лозунг, тенденцию развития. Конечно, нынешний ПРОЛОГ не обеспечивает декларативность и его логическая семантика работает только на простых примерах.
                  Как раз я показываю его явную императивность.
                  То, что во все модные языки вставляют средства работы со списками, говорит о неизбежности декларативного подхода, а не то что сейчас он доступен.
                  Появление языка Хаскел демонстрирует стремление обеспечить хотя бы декларативность на уровне статического контроля типов.


                  1. rg_software
                    12.01.2016 08:50

                    По правде говоря, я не вижу тенденции. Как некий способ программирования — конечно, да, а откуда тенденция?
                    Также я не вижу связи между списками и декларативным подходом. Список — это просто структура данных, такая же как массив или стек. Что в ней декларативного?


                    1. N_Ikhsanov
                      12.01.2016 09:29

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


                      1. rg_software
                        12.01.2016 20:55

                        Даже если я не соглашусь, мне интересно, как вы выводите эту тенденцию. Скажем, в C++ списки существуют со стандарта 1998 года официально, в Java примерно с того же времени. Специализированные инструменты тоже выбрасывались столько, сколько себя помню (ещё в вузе делали курсовики с помощью Silverrun — это CASE средство для разработки баз данных, тогда была особенно модная тема).


                  1. lair
                    12.01.2016 11:57
                    +2

                    То, что во все модные языки вставляют средства работы со списками, говорит о неизбежности декларативного подхода

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

                    (не говоря уже о том, что неплохо бы подтвердить утверждение «во все модные языки вставляют средства работы со списками» — лично я ему не верю, например)


                    1. N_Ikhsanov
                      12.01.2016 15:31

                      Может что-нибудь конструктивное скажете?


                      1. lair
                        12.01.2016 15:34
                        +2

                        Консктруктивность критики ложного утверждения состоит в девальвации этого утверждения.


    1. norguhtar
      11.01.2016 12:58

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


      1. N_Ikhsanov
        11.01.2016 13:33

        Попробуйте почитать ч.1.
        Конечно, еще лучше было бы с интерпретатором ПРОЛОГа сразу прогонять примеры.


        1. norguhtar
          11.01.2016 13:39

          Мне то зачем? У нас целый курс в универе по нему был.


          1. N_Ikhsanov
            11.01.2016 13:45

            Чтобы не выворачивать мышление.


            1. norguhtar
              11.01.2016 13:51

              В любом случае надо, так-как к императивному подходу, подход пролога никакого отношения не имеет.


              1. N_Ikhsanov
                11.01.2016 14:05

                А вот мне интересно, в каких случаях Вы сталкиваетесь с необходимостью обработки символьной информации в своей программе? Разве символьная обработка не будет у Вас отдельным набором модулей?


            1. MacIn
              11.01.2016 18:04

              Придется. Как-то Хорновские дизъюнкты и метод резолюций не очень похожи на привычное императивное программирование. Хотя они красивы.


  1. begemot_sun
    11.01.2016 15:01

    > В третьей части рассмотрим применение Пролога для построения одного типа интеллектуальных систем — экспертных систем.

    Ждем описание методов редукции дерева решений.


    1. N_Ikhsanov
      11.01.2016 15:07

      Готовьте вопросы.


  1. evocatus
    11.01.2016 17:08
    +1

    А я хочу сказать Вам спасибо за Ваши статьи — они меня сподвигли начать изучать Prolog (что было в моём списке дел уже полгода). Читая руководство по языку никак не мог понять вообще о чём речь (какие-то предикаты, атомы, арность). Поискал в Google «Prolog tutorial» и уже смог сделать простейшую программу.

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

    P.S. Если вы пытаетесь писать код в интерпретаторе и всё время получаете «uncaught exception: error...», то сохраните ваш код в файл, а затем импортируйте его командой consult или вот так: [«1.pl»].
    Насколько я понял, ошибка выскакивает (в GNU Prolog, yap и SWI Prolog) из-за того, что в интерпретаторе нельзя объявлять свои предикаты (что лично мне кажется неудобным маразмом).


    1. MacIn
      11.01.2016 18:06

      никак не мог понять вообще о чём речь (какие-то предикаты, атомы, арность

      Ищите «дизъюнкт Хорна» и «метод резолюций».

      напоминает навороченную булеву алгебру

      Ну… какбэ это оно и есть. Точнее, можно так условно сказать.


      1. N_Ikhsanov
        11.01.2016 19:15

        Не надо ни Хорна ни дизъюнктов! Специально написал эти статьи, чтобы показать, как работать с ПРОЛОГом без всей этой математической терминологии. Она нужна теоретикам, программистам она никак не помогает!


        1. MacIn
          11.01.2016 20:25
          +1

          Надо Хорна и метод резолюций. Без этого не понять, что это за язык и как он работает.

          Специально написал эти статьи, чтобы показать, как работать с ПРОЛОГом без всей этой математической терминологии

          Вы же видите — это только порождает вопросы «на фига козе баян» зачем нужен Пролог. Если бы вы начали с теории, было бы ясно.
          Она нужна теоретикам, программистам она никак не помогает!

          Глупость. Это элементарные понятия, и не зная их, в Пролог соваться незачем.


          1. N_Ikhsanov
            11.01.2016 20:29

            Всякое утверждение должно быть обосновано. Свое утверждение о ненужности дизъюнктов и понятия клауз Хорна я обосновываю этой публикацией.
            А Вы чем обосновываете свое утверждение? Тем что все учебники по ПРОЛОГу забиты этой терминологией, не так ли?


            1. MacIn
              11.01.2016 20:34
              +3

              Свое утверждение о ненужности дизъюнктов и понятия клауз Хорна я обосновываю этой публикацией.

              Нет, эта публикация не является обоснованием ненужности теории.
              Ваша публикация показывает, как делать на Прологе императивные штуки. Только и всего. И сразу возникают вопросы — посмотрите сами — а зачем извращаться, если можно императивные вещи делать на императивном языке. Вы не разъясняете, чем крут язык, и насколько он прост, вы запутываете.

              А Вы чем обосновываете свое утверждение? Тем что все учебники по ПРОЛОГу забиты этой терминологией, не так ли?

              Нет. Тем, что понимая принцип резолюций и дизъюнктов Хорна, суть Пролога объясняется в 2 строчки. Без кучи статей.
              Потом 1 пример решения простой задачи и человек все понял.
              Мое утверждение основано на личном опыте разъяснения другим, что такое Пролог.


              1. N_Ikhsanov
                11.01.2016 20:42

                А мое утверждение основано на десятилетнем опыте обучения студентов мехмата, которые на этом ПРОЛОГе делали курсовые и дипломные работы.


                1. MacIn
                  11.01.2016 20:50
                  +1

                  Вы же беретесь объяснить программистам?

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


                  1. N_Ikhsanov
                    11.01.2016 22:45

                    Какие расчеты? Пролог — для символьных вычислений, не для численных!


                    1. MacIn
                      11.01.2016 22:54

                      Я говорю «особенно». Можно сделать что угодно, но есть вещи, для которых Пролог не подходит.


    1. N_Ikhsanov
      11.01.2016 19:12

      А в понимании языка эти две статьи Вам не помогли? Задайте конкретные вопросы.
      В режиме интерпретации можно определять предикаты, загружая в память командой assert/1, но правильнее, конечно, записывать их в текстовый файл, потом делать reconsult.


      1. MacIn
        11.01.2016 20:31

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


        1. N_Ikhsanov
          11.01.2016 20:43

          В первой части как раз и приведен пример работающей программы решения головоломок.


          1. MacIn
            11.01.2016 21:19

            Да, пример есть, но я, пожалуй, погорячился говоря о «крохотном» примере.


      1. evocatus
        11.01.2016 21:52

        Я перестал понимать Вашу статью вот в этом месте

        а в третьем как [H|T] – произвольный непустой список.

        И до сих пор не понимаю как [H|T] может быть связано с произвольным непустым списком. Я до списков ещё не дошёл.
        Честно говоря, для меня кривая вхождения Вашей статьи оказалась слишком крутой.

        Когда начал смотреть самые простые туториалы, то понял, что Prolog просто инопланетный ЯП для человека с традиционным бэкграундом. Здесь всё настолько по-другому, что я не могу сравнивать Prolog ни с одним ЯП, о которых я хоть что-то знаю (а это десятка 2, не меньше).

        Попробую перечислить свои основные открытия на данный момент.

        1) Если имя начинается с большой буквы, то это переменная, если с маленькой, то это атом. Такое жёсткое вмешательство в именование очень непривычно.
        2) Точка обозначает конец правила
        3) Если описываем комплексное правило, состоящее из нескольких предикатов, то разделение их запятой играет роль логического И, а точкой — логического ИЛИ.
        4) Перемена мест параметров в факте имеет значение


        1. N_Ikhsanov
          11.01.2016 22:43

          В начале есть ключевая фраза: «Представьте программу, которая состоит только из вызовов процедур, а каждая процедура имеет несколько реализаций»


        1. MacIn
          11.01.2016 23:01
          +1

          И до сих пор не понимаю как [H|T] может быть связано с произвольным непустым списком. Я до списков ещё не дошёл.

          Это просто стандартное описание списка. Такой синтаксис. Если там [], то машина будет подставлять пустой список, если там [H|T] то непустой, разделяя голову H и остальное тело T. В ходе работы пролог машина будет пытаться подставлять (унифицировать) параметры. Такая запись говорит о том, что предикат применим для непустого списка, его можно подставить.

          Например:
          domains
            list=integer* /* Тип элемента списка - integer*/
          
          predicates
            sum_list(integer, list)
            sum_list(integer, integer, list)
          clauses
          
            sum_list(Summa, L) :- sum_list(Summa, 1, L). /* public */
          
            sum_list(0, Nom_el, []). /* граничный случай*/
          
          /* суммируем с нечетным элементом, иначе - отсечение */
            sum_list(Summa, Nom_el, [H|T]) :- Nom_el mod 2 <> 0,
          				    !,
          				    Nom_el_sub = Nom_el + 1,
          				    sum_list(PodSumma, Nom_el_sub, T),
          				    Summa = PodSumma + H.
          /* альтернатива - для четного элемента */
            sum_list(Summa, Nom_el, [H|T]) :- Nom_el_sub = Nom_el + 1,
          				    sum_list(Summa, Nom_el_sub, T).
          
          
          goal
          
            L=[2,4,3,1,7,2,4],
            sum_list(S,L),
            write("Summa = ",S).
          


          Вот этот предикат:
          sum_list(0, Nom_el, []).
          Применим только если мы пытаемся в третий элемент подставить пустой список, при этом «результатом» будет 0, а вот этот:

          /* суммируем с нечетным элементом, иначе — отсечение */
          sum_list(Summa, Nom_el, [H|T]) :- Nom_el mod 2 <> 0,
          !,
          Nom_el_sub = Nom_el + 1,
          sum_list(PodSumma, Nom_el_sub, T),
          Summa = PodSumma + H

          применим для любых непустых списков.


        1. N_Ikhsanov
          12.01.2016 09:10
          +1

          [H|T] — одно из ключевых выражений в языке ПРОЛОГ.
          Дело в том, что в нем усложнены такие вещи как переменная и присваивание.
          Вместо присваивания применяется унификация — аналог подстановки параметров при вызове процедуры.
          В ПРОЛОГе Вы можете использовать вместо «обычной» переменной некую конструкцию из нескольких переменных, по сути — это паттерн, задающий ограничения на фактический параметр.
          [H|T] — это две переменные, из которых первая — Н не имеет ограничений, а вторая -Т должна быть списком.
          Если в режиме интерпретации Вы введете, например такое выражение:
          [a,b,c,d] = [H|T]
          то система выдаст:
          H=a,
          T=[b,c,d]

          Применение [H|T] означает следующее:
          — в данную позицию можно подставить только непустой список (содержащий не менее одного элемента);
          — после выполнения унификации со списком H будет ссылаться на головной элемент подставленного списка, а T — на хвост этого списка.


  1. ForNeVeR
    12.01.2016 08:04

    Пожалуйста, пишите ещё статьи про Пролог. Язык этот очень интересный (и нестандартный для программиста, который в работе чаще сталкивается с мейнстримными языками), и с этим связано неоднозначное отношение сообщества к вашим публикациям (и — особенно — комментариям).

    Пишите ещё, многим такие вещи очень интересны!


    1. N_Ikhsanov
      12.01.2016 08:57
      +1

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


      1. MacIn
        12.01.2016 19:07

        Это уже интереснее и куда ближе к «духу» Пролога. Жду с нетерпением.


      1. a_bakshaev
        15.01.2016 02:56

        Напишите, если не сложно конкретный сеанс работы. Как скачать, установить, скомпилировать, запустить простейшую программу уровня hello,world на prolog.


        1. N_Ikhsanov
          15.01.2016 15:45

          Мне конечно, несложно это сделать. Но я сомневаюсь в актуальности такой темы, поскольку у каждой системы реализации языка ПРОЛОГ есть свои специфические особенности, и есть своя документация.
          Если есть еще желающие, могу написать такую заметку.


          1. a_bakshaev
            15.01.2016 23:27

            Хорошо, и статья про экспертную систему это отлично. Еще бы с обзором, что может экспертная система. К примеру, интересует задача автоматической SSE векторизации С кода. Интерфейса тут минимум, и как раз символьные данные на входе и выходе.