@N_Ikhsanov read-only
Пользователь
11 января в 09:15

Разработка → Парсинг на языке Prolog tutorial

Публикация первой части ( 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.

В третьей части рассмотрим применение Пролога для построения одного типа интеллектуальных систем — экспертных систем.
@N_Ikhsanov
карма
6,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

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

  • 0
    У пролога есть одна довольно значимая проблема. Сделать на нем какой либо интерфейс взаимодействия с пользователем тот еще цирк. Как итог его неплохо бы использовать на манер lua т.е. исключительно как встраиваемый язык. Я с ним имел дело довольно таки давно, как с этим сейчас?
    • 0
      В разных реализациях языка есть свои средства — встроенные предикаты для взаимодействия с внешней средой.
      Можно сделать и по другому — сделать на ПРОЛОГе бbблиотеку или DLL.
      • 0
        Можно сделать и по другому — сделать на ПРОЛОГе бbблиотеку или DLL.

        Опять же получается в лыжах на асфальте. В этом и проблема. А на чистом прологе писать ПО мягко говоря не очень. Это кстати в свое время сильно ограничивало использование как его так и лиспа.
        • 0
          ПРОЛОГ — язык специализированный — для задач обработки символьной информации и искусственного интеллекта.
          • 0
            Иии? Что толку от него если я им не могу воспользоваться, а средства ввода-вывода убоги до нельзя?
            • +1
              Разве Вы в своей работе пользуетесь только одним языком программирования?
              • 0
                Внимательно читаем первый коммент. Я указываю проблему Prolog это интеграция. Из-за этого его использование в том числе в полезных целях весьма затруднено.
                • +1
                  Может быть Вы не смотрели профессиональный ПРОЛОГ? Не вижу проблем с интеграцией.
                  • –1
                    Эм, а что такое профессиональный ПРОЛОГ? :)
                    • +1
                      Который продается, например SICStus Prolog.
                • 0
                  Я указываю проблему Prolog это интеграция.

                  Не занимался интеграцией с прологом, но первое, что приходит в голову — это передача итоговых данных по TCP на localhost. И это не единственный способ.
                  • 0
                    Во всех реализациях ПРОЛОГа большое внимание уделяется средствам интеграции, есть несколько вариантов.
                    Например, в ARITY/PROLOG есть просто встроенный Си, не говоря уже о том, что можно компилировать ПРОЛОГ в obj — файл, DLL или EXE.
                    • 0
                      Это довольно заморочно. И в случае встроенный C это выворачивание на изнанку. По мне хорошо именно бы сделать наоборот, когда у меня есть императивный язык который используется для GUI и прочих абстракций, а когда мне нужнен prolog то я его вызываю оттуда. Все можно компилировать пролог в ddl или exe это сложный путь.
                      • +1
                        На Прологе делаете DLL и вызываете из своей «обычной» программы.
                  • 0
                    Ну опять же tcp для пролога тоже не очень нативный метод :)
                    • 0
                      Если хочется, можно на ПРОЛОГе сделать EXE и вызывать его через средства управления процессами.
            • +1
              Метафорой отвечу: А как на языке мат алгебры можно ввод вывод писать???
              Языки предназначены для описания ограниченной предметной области.
    • 0
      Сделать на нем какой либо интерфейс взаимодействия с пользователем тот еще цирк.

      Есть Visual Prolog. Например, button::defaultAction :- Name = idc_ancestordialog_personname:getText(). В логической парадигме программирования нет присвоения переменных и вызовов функций, поэтому концептуально это можно перевести как «defaultAction истинно, когда Name совпадает с тем, что ввёл пользователь в окошко». Потом в каком-то месте программы утверждается, что defaultAction истинно (напомню, вызовов функций нет), и Prolog начинает искать все Name, которые совпадают с тем, что ввёл пользователь. Для этого у пользователя просят ввести данные. После этого считается, что Name определён. В общем-то да, GUI в логической парадигме немного запутаннее, чем в функциональной или объектно-ориентированной, но тем не менее, вполне осуществим.
      • 0
        Как итог имеем отдельную прослойку которая по сути императивна :) Я по этому и говорю, что более правильно prolog бы в виде встраиваемого языка использовать аля lua. В этом случае такой проблемы как работа с GUI нет :)
        • +1
          Ну да, пролог не для GUI. В программе, написанной на языке логического программирования, решение задачи часто ищется посредством хорошо оптимизированного брутфорса. Изначально это задумывалось как перебор математических фактов с целью доказательства теорем. Можно создать обычную прикладную программу на прологе, и она будет гораздо гибче и понятнее, чем программа из любой другой парадигмы, но будет она очень тормознутой — ибо брутфорс. Можно написать на прологе и быструю программу, но тогда нужно будет не просто описывать известные факты о системе, а как бы направлять ход выполнения программы, по сути возвращаясь к императивному программированию. Логическое программирование тем и ценно, что программист, например, описывает что такое ток, проводочки и диоды, задаёт максимальное количество элементов на микросхеме, и просит программу собрать ему синхрофазотрон. Без шуток, этот очень оптимизированный брутфорс творит чудеса, и Ватсон свидетель чудес его!
  • +1
    Вот вы пишете про лексический и синтаксический анализ на прологе. В чем его преимущества по сравнению с тем же ANTLR, особенно с учетом такого не слишком понятного синтаксиса?
    • –2
      Для понимания синтаксиса написана ч.1. ( habrahabr.ru/post/274603 )
      Существует множество всяких систем построения синтаксических анализаторов — сравнивать со всеми не имею возможности.
      Цель была — показать естественность операций, связанных с парсингом, в ПРОЛОГе, поскольку грамматика непосредственно отображается в язык.
      • 0
        В таком случае «естественный» парсинг подойдет и для Erlang и для Haskell. Там это будет более естественное, т.к. там тоже есть паттерн-матчинг.

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

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

    (возможно, правда, дело не в языке, а в том, как написаны примеры, спорить не буду)
    • +1
      согласен, все примеры можно было бы прогнать через highlighter
    • 0
      См ч.1
      • 0
        Там нет ответа на этот вопрос, либо я его не нашел. Можете привести цитату?
        • 0
          Цель первой части — описать Пролог с процедурной точки зрения, чтобы нетрудно было понять этот язык «обычному» программисту.
          • 0
            То, что вы где-то описали Пролог с процедурной точки зрения, не отвечает на мой вопрос, является ли Пролог сам по себе понятным языком.
            • +2
              Я считаю, да — является понятным языком. А Вы не можете прочесть (а не просто просмотреть) ч.1 и сказать свое впечатление по понятности описания языка?
              • 0
                Я же так и сделал. Код в этой (второй) статье понятнее не становится. Повторюсь, дело может быть не в языке, а конкретно в ваших примерах.
                • 0
                  А Вы не можете задать конкретный вопрос — что именно непонятно?
                  • 0
                    Да далеко не ходить: 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
                    
                    • 0
                      Сорри, задумался. Не [term(h); lexer(t)], а term(h) :: lexer(t)
                    • 0
                      lexr(S,R):- list_text(L,S), delb(L,L1),lexr1(L1,R),!..

                      list_text(L,S) — встроенный предикат, преобразует строку всписок кодов символов.
                      delb(L,L1) — удаление пробелов, описан ниже.
                      lexr1(L1,R) — основной предикат лексического анализа — описан ниже по тексту.
                      У Вас примерно такой же код по объему, только Вам он привычен.
                      • 0
                        Вопрос не объема, а понятности. Вам приходится описывать свой код словами, хотя можно было бы сделать его самоочевидным для всех, кто знает синтаксис языка.
                        • 0
                          Не знаю, что Вам ответить. Мне кажется, мы с Вами впадаем в холивар или по-русски — в перепалку.
                        • +1
                          Код понятен. То, что предикаты названы в стиле a1v25b9 — не вина языка.
                          • 0
                            Я про это с самого начала пишу.
                            • +1
                              Конечно, можно написать длинные и красивые имена предикатов — ограничений нет. Мне так не хочется делать, потому что имена предикатов в ПРОЛОГе повторяются чаще, чем в обычных языках.
                    • 0
                      предикат lexr/2 последовательно вызывает три предиката: list_text/2, delb/2, lexr1/2.

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

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

                        К сожалению, сути это не меняет. Современные языки позволяют многое из того, что показывает автор, записать понятнее и очевиднее.
                        • 0
                          Давайте сделаем так — Вы напишете эквивалентные программы на HASKELL, потом сравним.
                          • 0
                            А почему именно на Хаскеле, которого я еще и не знаю?

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

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

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

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

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


                                Где Rx.window — это вполне типовая операция над последовательностями, которая превращает последовательность в последовательность последовательностей, разделяемых по условию.
                                • 0
                                  Это называется фичи. В ПРОЛОГе тоже можно заготовить всякие такие стандартные предикаты и гордо ими козырять. Написать настраиваемый лексический анализатор, как стандартный предикат недолго.
                                  Вот сегодня только напечатали заметку, что происходит с языками, которые бесконечно расширяют встроенными процедурами.
                                  Вы напишите всю программу с синтаксическим анализом, деревом и вычислением значения. И без готовых фич.
                                  • +1
                                    Нет, это называется «компонуемость». Я беру стандартную функцию, отвечающую за фильтрацию элементов в последовательности, присоединяю ее к стандартной же функции, отвечающей за разбиение последовательности по условию — и получаю, внезапно, лексер. А теперь, внимание, вопрос: насколько сложно добиться такой же компонуемости (особенно в части передачи предикатов внутрь других предикатов) в прологе?
                                    • 0
                                      Что тут обсуждать? ПРОЛОГ — полиморфный язык. Если очень хочется, в основной части языка есть стандартная операция call X, где X — переменная, которой можно динамически присваивать значение — любой предикат, точнее, цель.
                                      Я Вам стремлюсь показать природу языка, а ВЫ говорите о фичах. Пишите фичи сколько хотите, потом еще можете сказать, что у Вы создали новый язык программирования, лучше ПРОЛОГа.
                                      • +3
                                        Я Вам стремлюсь показать природу языка

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

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

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

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

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

                                            Вот только в моем примере не было ничего, подходящего под это определение.
                                    • 0
                                      У меня еще возник вопрос — интерес к символьным языкам у Вас чисто академический или он связан с работой? Если Вы практически такими задачами заняты, может лучше Ваши задачи обсудить, точнее, способы их решения.
                                      • 0
                                        У меня любой интерес к языкам программирования связан с работой: я на каждый из них смотрю с точки зрения «есть ли у меня задачи, которые он решит эффективнее, чем тот инструментарий, которым я уже пользуюсь». В случае ваших статей о прологе ответ скорее «нет»; более того, для задач парсинга и интерпретации входного потока, если у меня возникнет такая необходимость, я явно возьму что-то другое.
                                        • 0
                                          Язык F#, как я понял по синтаксису Вашего примера — микрософтовская реализация языка HASKELL.
                                          Почему-то многие, встречая что-то новое, включают защитный рефлекс.
                                          Мне хотелось просто показать естественность и простоту там, где обычно читателя смущают новой непонятной терминологией.
                                          • 0
                                            Язык F#, как я понял по синтаксису Вашего примера — микрософтовская реализация языка HASKELL.

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

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

                                            И снова, вам это не удалось. Ваши примеры не выглядят ни естественными, ни простыми.
                                          • 0
                                            F# это «реализация» OCaml в .NET
                            • 0
                              Что я могу ответить, если не знаком с этой системой? Да и какая разница — ведь таких систем генерации полезных программ множество.
                              • 0
                                Что я могу ответить, если не знаком с этой системой?

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

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

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

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

                                    Вы меня извините, но демонстрировать природу языка из логической парадигмы на функциональной (с определенным входом и выходом) задаче — не самая хорошая идея.
                                    • 0
                                      Может Вам это и не надо — у Вас есть привычный набор инструментов. Предложите что-нибудь конструктивное.
                                      • 0
                                        А уже предложили выше: расскажите, в чем преимущества пролога для такой задачи по сравнению с другими инструментами.
                                        • 0
                                          Изучать другие инструменты мне?
                                          • 0
                                            Конечно, вам. Вы же зачем-то утверждаете, что пролог для этой задачи хорошо подходит.
                                    • 0
                                      А Вы не хотите подумать об интеллектуализации системы? Например, дать полную свободу в заказе отчетов пользователю. Насколько я знаю, в большинстве корпораций в отчетную пору пользователям требуется помощь программистов.
                                      • 0
                                        Уже подумали, отсюда и возникла задача «построение отчетов на основе SQL-подобного языка». Выражение для построения отчета пишет пользователь.
                                        • 0
                                          А пользователь должен его выучить? И консультироваться с программистами? А если сделать так, чтобы пользователю не требовались консультации и обучение?
                                          • 0
                                            А пользователь должен его выучить?

                                            Да.

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

                                            В общем случае это невозможно — по крайней мере, в разумной перспективе развития этой системы (3-5 лет).
                                            • 0
                                              Экспертные системы давно придумали.
                                              • 0
                                                И что? Для того, чтобы они давали нужный пользователю результат, ему (пользователю) все равно нужно обучиться работать с такой системой.
                                                • 0
                                                  Хотел посоветовать Вам глубже изучить идею ЭС, но думаю пока это будет бесполезно, т.к. прошло слишком много лет с появления этой идеи и ее сильно исказили.
                                                  • 0
                                                    Ну вот видите, а вы говорите — давно придумали. Придумать-то придумали, да исказили, потому и взять прямо сейчас нечего.
                                                    • 0
                                                      Вы хотите готовый инструмент. А что-то подобное самому сочинить?
                                                      • 0
                                                        Я предпочитаю тратить свое свободное время на что-нибудь другое. А бюджет в проекте на это, предсказуемо, никто не даст.
                                                      • 0
                                                        Мне кажется, вы неявно предполагаете, что основная проблема пользователя — это выучить SQL-подобный (или любой другой) язык программировния, чтобы генерировать отчёты и тому подобное. Это ведь тоже довольно старая мысль: давайте вместо Pascal/Java/etc. дадим пользователю нечто вроде русского языка или вообще графические блоки, он будет соединять их стрелочками, и наступит всеобщее счастье.

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

                                                        Я уж не говорю о том, что пользователь сам может убраться в офисе и приготовить обед, но смысла в этом мало: у пользователя своя работа, у повара своя, а у программиста своя, и лучше каждому делать то, в чём он лучше всего разбирается, и сотрудничать на всеобщее благо. В конце концов, за фразой «требуется консультация с программистом» не скрывается никакой особенной трагедии: открыл скайп и проконсультировался, всего-то дел.
                                                        • –1
                                                          Основная проблема программистов — в алгоритмическом мышлении. Почему-то они думают, что пользователь должен мыслить алгоритмически. Нет, конечный пользователь знает свою работу и не должен быть каким-то слабым программистом на упрощенном языке интерфейса, который ему дает программист. Пользователь должен описать свою проблему, а вот превратить ее в программу — задача программиста. Пользователь должен описывать ЧТО ему требуется, а программисты дают ему такие средства, что пользователь должен дополнительно учиться описывать не свою задачу, а способ ее решения, последовательность действий через интерфейс, предоставленный ему программистом.
                                                          • 0
                                                            Между пользователем и собственно программистом вы забываете постановщиков задачи, проектировщиков, архитекторов. Это их дело — выяснить, что пользователю нужно, и переложить в ТЗ для программиста.
                                                          • +2
                                                            Не согласен.
                                                            Проблема в том, что пользователь действительно должен, но не может описать свою проблему с достаточным количеством деталей для её решения.

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

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

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

                          Просто потому что автор избрал не ту тактику :) Он вместо того, чтобы показать задачи, под кооторые Пролог «заточен», показывает решение привычных в императивной парадигме вещей. Но посколькку они императивны, то в Прологе выглядят плохо. Только и всего.
                          • 0
                            Тут немедленно возникает разумный вопрос: под какие же задачи пролог заточен?
                            • +2
                              На мой взгляд — проверка набора фактов. Это его основа. Вы можете сделать это императивно, но на Прологе это выглядит еще и понятно.
                            • +1
                              Тут немедленно возникает разумный вопрос: под какие же задачи пролог заточен?
                              Вот этот комментарий частично отвечает на ваш вопрос. Пример автоматического доказательства теоремы можете посмотреть в этой статье, я тоже на эту тему писал.
                          • 0
                            В ПРОЛОГе точно также можно заготовить всякие типовые процедуры, а затем вызывать их в две строки.
                            ПРОЛОГ предназначен для работы с символьной информацией и для решения задач, требующих логического вывода.
                            Заготовить процедуры на все случаи жизни — это делает любой программист и не надо называть это «заточенностью.»
                            • +1
                              Заготовленные процедуры здесь ни при чем. Пролог позволяет нам проверить набор фактов — это его задача, для этого язык создан. То, что мы можем, используя императивную сущность пролог-машины и отсечение, делать императивные вещи — отдельный разговор.
                              Вы можете написать на html/css (чисто декларативном языке) игру, которая будет интерактивной. Потому что браузер, интерпретирующий html и css, императивен внутри. Это не отменяет того, что html — язык разметки, он под это «заточен».
                    • 0
                      А еще в ней, на самом деле, происходит последовательное присвоение (не уверен, что это правильный термин, правда) результатов предикатов для их использования в следующем предикате

                      Это издержки императивности реализации. Правильный термин — подстановка ЕМНИП.
                      • 0
                        Правильный термин — унификация, которая по своей природе является подстановкой аналогично тому как подставляются ссылочные фактические параметры при вызове процедуры.
                        • 0
                          Да, точно. Унификация.
          • +1
            Зачем описывать Пролог с процедурной точки зрения, если у него совершенно другая вычислительная модель и он решает совершенно другие задачи? Тем более, что сам язык объективно мертв по очень простой причине — реализовать прологовскую модель вычислений (а точнее целое семейство алгоритмов родившихся из нее) под конкретную задачу на любом современном императивном/функциональном проще чем пытаться родить на прологе то для чего он не предназначен.
            • 0
              Во всех учебниках ПРОЛОГ описывается с двух точек зрения — декларативной парадигмы и процедурной.
              Но декларативная (или логическая) семантика не полностью реализована в языке и только затуманивает понимание языка, если мы хотим на нем делать реальные программы.
              • +2
                Правильно. Но пролог ценен только своей декларативной частью. Все остальное — набор ужасных и устаревших костылей нужных как раз для того, чтобы на прологе можно было решать не только узкий набор задач. Вопрос остается тот же — зачем мучатся с прологовскими костылями, если ценная его часть элементарно реализуется под конкретную задачу в любом современном языке?
                • –1
                  Если куски ПРОЛОГа вставляют в эти необъятные монстры — нагромождения бесконечного количества библиотек, называемые современными языками программирования, то чем тут гордиться?
                  Вам показывают суть парадигмы логического программирования, а стандартные процедуры есть в каждой реализации — можете на них посмотреть, если хочется.
                  • 0
                    Причем здесь стандартные процедуры?
                    Плохо понимаю причем тут нагромождения библиотек и как они связаны с языками программирования.

                    Суть мне показывать не надо, я когда-то изучал пролог, переизобрел его когда игрался с написанием парсера команд на «естественном языке», написал еще один логический движок для решения задачи планирования действий агентов (потому что типовой прологовский «решатель» задачу не решал в силу определенных ограничений). Тем более что вы почему-то показываете логическую парадигму на чисто функциональной задаче от чего у людей и взрывается мозг.
                    • 0
                      Синтаксический анализ — функциональная задача?
                      • +1
                        Если понимать под синтаксическим анализом рекурсивный обход дерева — то да. Заставлять пролог выполнять тот же обход через решение графа отношений — не очень хорошая идея.
                        • 0
                          Разве ПРОЛОГ делает это неэффективно? Там простой перебор вариантов.
                          • 0
                            Вопрос не в эффективности, а в понятности, наглядности и той самой декларативности кода.
                          • 0
                            Вообще, «простой перебор вариантов» — далеко не всегда самый эффективный способ решения задачи.
              • 0
                Как-то вы себе противоречите. В комментах к первой части это же ваша реплика: «Пора уже понять, что процедурный подход в программировании — временное явление. Все модные языки — это сдача позиций перед неизбежностью декларативности символьных структур.»

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

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

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

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

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

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

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

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

                        В аналоговых устройствах тоже есть задержка на распространение и преобразование сигнала. Если взять 2 рации, то звук из второй появится не одновременно с тем, как мы скажем что-то в первую. При проектировании устройств учитывается направление и движение тока.
                        • 0
                          А дождь падает с неба на землю. К чему трюизмы?
                          Про распространение сигнала — вы немного уходите не в ту степь. Да, задержка есть, но это не сравнимо с итеративным подходом. Для выполнения двух действий на итеративной машине мы всегда сначала выполним одно, затем другое. То, что они могут занимать разное время — про это я не говорю. В аналоговой системе если вы подключите к источнику сигнала 2 одинаковых блока (не реальных, а идеалистичных), то сигнал до них дойдет одновременно и обрабатывать они его будут одновременно. Это не отменяет задержек, но я по-русски написал «условно». Можно было говорить о многоядерной системе и параллельных вычислениях, или просто комбинационных схемах.
                  • 0
                    Зачем эти спекуляции на тему декларативности? Если Вы внимательно прочитали текст — моя цель показать две вещи:
                    — в ПРОЛОГе нет ничего сверхсложного и всякий программист может его применять:
                    — есть задачи задачи, которые на ПРОЛОГе делать лучше, чем в императивном языке.
                    • 0
                      Это не про вашу статью сказано, не заводитесь.
                • 0
                  Я написал лозунг, тенденцию развития. Конечно, нынешний ПРОЛОГ не обеспечивает декларативность и его логическая семантика работает только на простых примерах.
                  Как раз я показываю его явную императивность.
                  То, что во все модные языки вставляют средства работы со списками, говорит о неизбежности декларативного подхода, а не то что сейчас он доступен.
                  Появление языка Хаскел демонстрирует стремление обеспечить хотя бы декларативность на уровне статического контроля типов.
                  • 0
                    По правде говоря, я не вижу тенденции. Как некий способ программирования — конечно, да, а откуда тенденция?
                    Также я не вижу связи между списками и декларативным подходом. Список — это просто структура данных, такая же как массив или стек. Что в ней декларативного?
                    • 0
                      Тенденция, конечно не так заметна на отрезке за 10 лет, надо смотреть дальше. То что корпорации выбрасывают на рынок все новые и новые специализированные инструменты — это стремление решить принципиальные проблемы программирования экстенсивным путем.
                      По поводу декларативности списка — это конечно неявно, список — более абстрактное понятие, чем массив или файл, поскольку его размерность и структура не регламентируется, а рекурсивность понятия список тоже более абстрактная, чем массив.
                      Вряд ли Вы со мной согласитесь — это мои оценки.
                      • 0
                        Даже если я не соглашусь, мне интересно, как вы выводите эту тенденцию. Скажем, в C++ списки существуют со стандарта 1998 года официально, в Java примерно с того же времени. Специализированные инструменты тоже выбрасывались столько, сколько себя помню (ещё в вузе делали курсовики с помощью Silverrun — это CASE средство для разработки баз данных, тогда была особенно модная тема).
                  • +2
                    То, что во все модные языки вставляют средства работы со списками, говорит о неизбежности декларативного подхода

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

                    (не говоря уже о том, что неплохо бы подтвердить утверждение «во все модные языки вставляют средства работы со списками» — лично я ему не верю, например)
                    • 0
                      Может что-нибудь конструктивное скажете?
                      • +2
                        Консктруктивность критики ложного утверждения состоит в девальвации этого утверждения.
    • 0
      Он вполне понятен, но надо погрузиться в тему. Плюс там та же проблема что и с функциональными языками, придется вывернуть мышление.
      • 0
        Попробуйте почитать ч.1.
        Конечно, еще лучше было бы с интерпретатором ПРОЛОГа сразу прогонять примеры.
        • 0
          Мне то зачем? У нас целый курс в универе по нему был.
          • 0
            Чтобы не выворачивать мышление.
            • 0
              В любом случае надо, так-как к императивному подходу, подход пролога никакого отношения не имеет.
              • 0
                А вот мне интересно, в каких случаях Вы сталкиваетесь с необходимостью обработки символьной информации в своей программе? Разве символьная обработка не будет у Вас отдельным набором модулей?
            • 0
              Придется. Как-то Хорновские дизъюнкты и метод резолюций не очень похожи на привычное императивное программирование. Хотя они красивы.
  • 0
    > В третьей части рассмотрим применение Пролога для построения одного типа интеллектуальных систем — экспертных систем.

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

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

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

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

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

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

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

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

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

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

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

              Нет. Тем, что понимая принцип резолюций и дизъюнктов Хорна, суть Пролога объясняется в 2 строчки. Без кучи статей.
              Потом 1 пример решения простой задачи и человек все понял.
              Мое утверждение основано на личном опыте разъяснения другим, что такое Пролог.
              • 0
                А мое утверждение основано на десятилетнем опыте обучения студентов мехмата, которые на этом ПРОЛОГе делали курсовые и дипломные работы.
                • +1
                  Вы же беретесь объяснить программистам?

                  Возможно, эти же работы были бы более элегантными на других языках. Особенно, если это программы расчетов.
                  • 0
                    Какие расчеты? Пролог — для символьных вычислений, не для численных!
                    • 0
                      Я говорю «особенно». Можно сделать что угодно, но есть вещи, для которых Пролог не подходит.
    • 0
      А в понимании языка эти две статьи Вам не помогли? Задайте конкретные вопросы.
      В режиме интерпретации можно определять предикаты, загружая в память командой assert/1, но правильнее, конечно, записывать их в текстовый файл, потом делать reconsult.
      • 0
        В сто раз больше, чем обе статьи, сделает один крохотный пример решения логической задачи на Прологе. Когда нам в свое время читали курс по Прологу, после изучения мат. теории и одного примера решения логической головоломки, все было понятно.
        • 0
          В первой части как раз и приведен пример работающей программы решения головоломок.
          • 0
            Да, пример есть, но я, пожалуй, погорячился говоря о «крохотном» примере.
      • 0
        Я перестал понимать Вашу статью вот в этом месте
        а в третьем как [H|T] – произвольный непустой список.

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

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

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

        1) Если имя начинается с большой буквы, то это переменная, если с маленькой, то это атом. Такое жёсткое вмешательство в именование очень непривычно.
        2) Точка обозначает конец правила
        3) Если описываем комплексное правило, состоящее из нескольких предикатов, то разделение их запятой играет роль логического И, а точкой — логического ИЛИ.
        4) Перемена мест параметров в факте имеет значение
        • 0
          В начале есть ключевая фраза: «Представьте программу, которая состоит только из вызовов процедур, а каждая процедура имеет несколько реализаций»
        • +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
          [H|T] — одно из ключевых выражений в языке ПРОЛОГ.
          Дело в том, что в нем усложнены такие вещи как переменная и присваивание.
          Вместо присваивания применяется унификация — аналог подстановки параметров при вызове процедуры.
          В ПРОЛОГе Вы можете использовать вместо «обычной» переменной некую конструкцию из нескольких переменных, по сути — это паттерн, задающий ограничения на фактический параметр.
          [H|T] — это две переменные, из которых первая — Н не имеет ограничений, а вторая -Т должна быть списком.
          Если в режиме интерпретации Вы введете, например такое выражение:
          [a,b,c,d] = [H|T]
          то система выдаст:
          H=a,
          T=[b,c,d]

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

    Пишите ещё, многим такие вещи очень интересны!
    • +1
      Я буду продолжать. Следующая статья будет о применении ПРОЛОГа в искусственном интеллекте.
      А Вы задавайте больше конкретных вопросов по тексту, пожалуйста.
      Очень много любителей глобальных тем.
      • 0
        Это уже интереснее и куда ближе к «духу» Пролога. Жду с нетерпением.
      • 0
        Напишите, если не сложно конкретный сеанс работы. Как скачать, установить, скомпилировать, запустить простейшую программу уровня hello,world на prolog.
        • 0
          Мне конечно, несложно это сделать. Но я сомневаюсь в актуальности такой темы, поскольку у каждой системы реализации языка ПРОЛОГ есть свои специфические особенности, и есть своя документация.
          Если есть еще желающие, могу написать такую заметку.
          • 0
            Хорошо, и статья про экспертную систему это отлично. Еще бы с обзором, что может экспертная система. К примеру, интересует задача автоматической SSE векторизации С кода. Интерфейса тут минимум, и как раз символьные данные на входе и выходе.

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