Цель была – показать опытным, и не очень, программистам, что ничего сложного в Прологе нет, и каждый может его применять в работе.
Почему-то не было вопросов непосредственно по тексту публикации. Буду думать, что там все понятно.
Приступим к рассмотрению более практических аспектов программирования на языке Пролог.
В этом разделе рассмотрим основные аспекты реализации на языке ПРОЛОГ трансляции контекстно-свободных языков на примере арифметических выражений.
Как известно, 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)
KvanTTT
11.01.2016 11:23+1Вот вы пишете про лексический и синтаксический анализ на прологе. В чем его преимущества по сравнению с тем же ANTLR, особенно с учетом такого не слишком понятного синтаксиса?
N_Ikhsanov
11.01.2016 12:45-2Для понимания синтаксиса написана ч.1. ( habrahabr.ru/post/274603 )
Существует множество всяких систем построения синтаксических анализаторов — сравнивать со всеми не имею возможности.
Цель была — показать естественность операций, связанных с парсингом, в ПРОЛОГе, поскольку грамматика непосредственно отображается в язык.begemot_sun
11.01.2016 14:40В таком случае «естественный» парсинг подойдет и для Erlang и для Haskell. Там это будет более естественное, т.к. там тоже есть паттерн-матчинг.
Так какова же киллер-фича для построения лексеров/парсеров? мне кажется в разрезе Erlang/Haskell на Prolog это делается сложнее.
N_Ikhsanov
11.01.2016 14:45В ПРОЛОГе — это DCG.
А Вы не могли бы уточнить, почему «в разрезе Erlang/Haskell на Prolog это делается сложнее.»?
lair
11.01.2016 12:11Вы правда считаете это понятным языком?
(возможно, правда, дело не в языке, а в том, как написаны примеры, спорить не буду)N_Ikhsanov
11.01.2016 12:46См ч.1
lair
11.01.2016 12:47Там нет ответа на этот вопрос, либо я его не нашел. Можете привести цитату?
N_Ikhsanov
11.01.2016 13:29Цель первой части — описать Пролог с процедурной точки зрения, чтобы нетрудно было понять этот язык «обычному» программисту.
lair
11.01.2016 13:35То, что вы где-то описали Пролог с процедурной точки зрения, не отвечает на мой вопрос, является ли Пролог сам по себе понятным языком.
N_Ikhsanov
11.01.2016 14:02+2Я считаю, да — является понятным языком. А Вы не можете прочесть (а не просто просмотреть) ч.1 и сказать свое впечатление по понятности описания языка?
lair
11.01.2016 14:18Я же так и сделал. Код в этой (второй) статье понятнее не становится. Повторюсь, дело может быть не в языке, а конкретно в ваших примерах.
N_Ikhsanov
11.01.2016 14:19А Вы не можете задать конкретный вопрос — что именно непонятно?
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
N_Ikhsanov
11.01.2016 14:56lexr(S,R):- list_text(L,S), delb(L,L1),lexr1(L1,R),!..
list_text(L,S) — встроенный предикат, преобразует строку всписок кодов символов.
delb(L,L1) — удаление пробелов, описан ниже.
lexr1(L1,R) — основной предикат лексического анализа — описан ниже по тексту.
У Вас примерно такой же код по объему, только Вам он привычен.lair
11.01.2016 14:58Вопрос не объема, а понятности. Вам приходится описывать свой код словами, хотя можно было бы сделать его самоочевидным для всех, кто знает синтаксис языка.
N_Ikhsanov
11.01.2016 15:06Не знаю, что Вам ответить. Мне кажется, мы с Вами впадаем в холивар или по-русски — в перепалку.
MacIn
11.01.2016 18:00+1Код понятен. То, что предикаты названы в стиле a1v25b9 — не вина языка.
lair
11.01.2016 18:03Я про это с самого начала пишу.
N_Ikhsanov
11.01.2016 18:51+1Конечно, можно написать длинные и красивые имена предикатов — ограничений нет. Мне так не хочется делать, потому что имена предикатов в ПРОЛОГе повторяются чаще, чем в обычных языках.
FiresShadow
11.01.2016 15:09предикат lexr/2 последовательно вызывает три предиката: list_text/2, delb/2, lexr1/2.
Эта фраза не совсем корректна, в логической парадигме нет понятия «вызов функции» или вызова чего бы то ни было. Точно также нет присвоения переменных.
Согласен, имена предикатов в данном случае малоинформативные.lair
11.01.2016 15:39Эта фраза не совсем корректна, в логической парадигме нет понятия «вызов функции» или вызова чего бы то ни было. Точно также нет присвоения переменных.
Да, я понимаю это, но опыта правильно это выразить мне не хватает.
К сожалению, сути это не меняет. Современные языки позволяют многое из того, что показывает автор, записать понятнее и очевиднее.N_Ikhsanov
11.01.2016 15:45Давайте сделаем так — Вы напишете эквивалентные программы на HASKELL, потом сравним.
lair
11.01.2016 15:47А почему именно на Хаскеле, которого я еще и не знаю?
(впрочем, пустой вопрос, я не уверен, что есть смысл это на любом языке делать: у вас выше спросили про разницу с ANTLR, вы от ответа ушли)N_Ikhsanov
11.01.2016 15:52Разве Ваши примеры:
let lexer input =
let rec lexer input =
//очень грубый пример, просто чтобы показать матчинг и рекурсию
match input with
| [] -> []
| [h; t] -> [term(h); lexer(t)]
не на этом языке приводятся?
Если это другой язык — можно на нем написать.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
— это вполне типовая операция над последовательностями, которая превращает последовательность в последовательность последовательностей, разделяемых по условию.N_Ikhsanov
11.01.2016 18:57Это называется фичи. В ПРОЛОГе тоже можно заготовить всякие такие стандартные предикаты и гордо ими козырять. Написать настраиваемый лексический анализатор, как стандартный предикат недолго.
Вот сегодня только напечатали заметку, что происходит с языками, которые бесконечно расширяют встроенными процедурами.
Вы напишите всю программу с синтаксическим анализом, деревом и вычислением значения. И без готовых фич.lair
11.01.2016 19:05+1Нет, это называется «компонуемость». Я беру стандартную функцию, отвечающую за фильтрацию элементов в последовательности, присоединяю ее к стандартной же функции, отвечающей за разбиение последовательности по условию — и получаю, внезапно, лексер. А теперь, внимание, вопрос: насколько сложно добиться такой же компонуемости (особенно в части передачи предикатов внутрь других предикатов) в прологе?
N_Ikhsanov
11.01.2016 20:06Что тут обсуждать? ПРОЛОГ — полиморфный язык. Если очень хочется, в основной части языка есть стандартная операция call X, где X — переменная, которой можно динамически присваивать значение — любой предикат, точнее, цель.
Я Вам стремлюсь показать природу языка, а ВЫ говорите о фичах. Пишите фичи сколько хотите, потом еще можете сказать, что у Вы создали новый язык программирования, лучше ПРОЛОГа.lair
11.01.2016 21:01+3Я Вам стремлюсь показать природу языка
Пока что вам это плохо удается. FiresShadow это удалось намного лучше.
а ВЫ говорите о фичах
Я не знаю, что вы понимаете под словом «фича», но, похоже, совсем не то, что я.N_Ikhsanov
11.01.2016 22:50firesshadow дал хорошее описание в стиле «смотри как бывает!». Моя цель — «смотри как ты можешь сделать!»
А под словом «фича» я понимаю специализированные процедуры, встраиваемые в язык.lair
11.01.2016 22:56+2Моя цель — «смотри как ты можешь сделать!»
… как я уже говорил, ваше «смотри» показывает мне вещи, которые я могу сделать на другом языке как минимум проще, и, скорее всего, лучше.
А под словом «фича» я понимаю специализированные процедуры, встраиваемые в язык.
Вот только в моем примере не было ничего, подходящего под это определение.
N_Ikhsanov
11.01.2016 20:09У меня еще возник вопрос — интерес к символьным языкам у Вас чисто академический или он связан с работой? Если Вы практически такими задачами заняты, может лучше Ваши задачи обсудить, точнее, способы их решения.
lair
11.01.2016 21:02У меня любой интерес к языкам программирования связан с работой: я на каждый из них смотрю с точки зрения «есть ли у меня задачи, которые он решит эффективнее, чем тот инструментарий, которым я уже пользуюсь». В случае ваших статей о прологе ответ скорее «нет»; более того, для задач парсинга и интерпретации входного потока, если у меня возникнет такая необходимость, я явно возьму что-то другое.
N_Ikhsanov
11.01.2016 22:54Язык F#, как я понял по синтаксису Вашего примера — микрософтовская реализация языка HASKELL.
Почему-то многие, встречая что-то новое, включают защитный рефлекс.
Мне хотелось просто показать естественность и простоту там, где обычно читателя смущают новой непонятной терминологией.lair
11.01.2016 22:58Язык F#, как я понял по синтаксису Вашего примера — микрософтовская реализация языка HASKELL.
Вы поняли неправильно.
Мне хотелось просто показать естественность и простоту там,
И снова, вам это не удалось. Ваши примеры не выглядят ни естественными, ни простыми.
N_Ikhsanov
11.01.2016 22:57Что я могу ответить, если не знаком с этой системой? Да и какая разница — ведь таких систем генерации полезных программ множество.
lair
11.01.2016 23:00Что я могу ответить, если не знаком с этой системой?
Сравните с другими современными распространенными системами парсинга, не суть. Просто без такого сравнения — как вы можете говорить, что пролог чем-то лучше?N_Ikhsanov
12.01.2016 07:46Мой тезис в том, что все системы парсинга основаны на использовании заготовленных библиотек соответствующих процедур, т.е. это просто парсеры с параметрами. Пользователь задает параметры и наслаждается результатом. Суть, природу языка демонстрируем, а не спорим, какой инструмент разработки лучше. Я показываю двигатель, а Вы мне — автомобиль.
lair
12.01.2016 11:54+1Мой тезис в том, что все системы парсинга основаны на использовании заготовленных библиотек соответствующих процедур, т.е. это просто парсеры с параметрами. Пользователь задает параметры и наслаждается результатом.
И чем это отличается от Пролога? Тем, что «параметры» описываются в DCG?
Понимаете, это все — инструменты. У меня есть информационная система, типичный LOB, ввод данных и отчетики. И мне надо добавить в нее построение отчетов на основе SQL-подобного языка. Соответственно, мне нужно распарсить входную строку до AST. Я могу взять пролог и встроить его к себе, а могу взять antlr/lex+yacc/younameit и встроить их к себе. Для решения этой задачи пролог — всего лишь один из инструментов, совершенно одинаковый в ряду прочих, и их сравнение полностью оправдано.
Суть, природу языка демонстрируем
Вы меня извините, но демонстрировать природу языка из логической парадигмы на функциональной (с определенным входом и выходом) задаче — не самая хорошая идея.N_Ikhsanov
12.01.2016 15:25Может Вам это и не надо — у Вас есть привычный набор инструментов. Предложите что-нибудь конструктивное.
lair
12.01.2016 15:33А уже предложили выше: расскажите, в чем преимущества пролога для такой задачи по сравнению с другими инструментами.
N_Ikhsanov
12.01.2016 15:34Изучать другие инструменты мне?
lair
12.01.2016 15:35Конечно, вам. Вы же зачем-то утверждаете, что пролог для этой задачи хорошо подходит.
N_Ikhsanov
12.01.2016 15:29А Вы не хотите подумать об интеллектуализации системы? Например, дать полную свободу в заказе отчетов пользователю. Насколько я знаю, в большинстве корпораций в отчетную пору пользователям требуется помощь программистов.
lair
12.01.2016 15:33Уже подумали, отсюда и возникла задача «построение отчетов на основе SQL-подобного языка». Выражение для построения отчета пишет пользователь.
N_Ikhsanov
12.01.2016 15:38А пользователь должен его выучить? И консультироваться с программистами? А если сделать так, чтобы пользователю не требовались консультации и обучение?
lair
12.01.2016 15:40А пользователь должен его выучить?
Да.
А если сделать так, чтобы пользователю не требовались консультации и обучение?
В общем случае это невозможно — по крайней мере, в разумной перспективе развития этой системы (3-5 лет).N_Ikhsanov
12.01.2016 15:42Экспертные системы давно придумали.
lair
12.01.2016 15:43И что? Для того, чтобы они давали нужный пользователю результат, ему (пользователю) все равно нужно обучиться работать с такой системой.
N_Ikhsanov
12.01.2016 15:53Хотел посоветовать Вам глубже изучить идею ЭС, но думаю пока это будет бесполезно, т.к. прошло слишком много лет с появления этой идеи и ее сильно исказили.
lair
12.01.2016 15:54Ну вот видите, а вы говорите — давно придумали. Придумать-то придумали, да исказили, потому и взять прямо сейчас нечего.
N_Ikhsanov
12.01.2016 15:58Вы хотите готовый инструмент. А что-то подобное самому сочинить?
lair
12.01.2016 16:01Я предпочитаю тратить свое свободное время на что-нибудь другое. А бюджет в проекте на это, предсказуемо, никто не даст.
rg_software
12.01.2016 20:52Мне кажется, вы неявно предполагаете, что основная проблема пользователя — это выучить SQL-подобный (или любой другой) язык программировния, чтобы генерировать отчёты и тому подобное. Это ведь тоже довольно старая мысль: давайте вместо Pascal/Java/etc. дадим пользователю нечто вроде русского языка или вообще графические блоки, он будет соединять их стрелочками, и наступит всеобщее счастье.
Да вот практика показывает, что основная проблема не в языке, а в программистском способе мышления, которым необходимо овладеть, чтобы рисовать правильные стрелочки, так что всё равно определённый тренинг здесь необходим.
Я уж не говорю о том, что пользователь сам может убраться в офисе и приготовить обед, но смысла в этом мало: у пользователя своя работа, у повара своя, а у программиста своя, и лучше каждому делать то, в чём он лучше всего разбирается, и сотрудничать на всеобщее благо. В конце концов, за фразой «требуется консультация с программистом» не скрывается никакой особенной трагедии: открыл скайп и проконсультировался, всего-то дел.N_Ikhsanov
15.01.2016 15:42-1Основная проблема программистов — в алгоритмическом мышлении. Почему-то они думают, что пользователь должен мыслить алгоритмически. Нет, конечный пользователь знает свою работу и не должен быть каким-то слабым программистом на упрощенном языке интерфейса, который ему дает программист. Пользователь должен описать свою проблему, а вот превратить ее в программу — задача программиста. Пользователь должен описывать ЧТО ему требуется, а программисты дают ему такие средства, что пользователь должен дополнительно учиться описывать не свою задачу, а способ ее решения, последовательность действий через интерфейс, предоставленный ему программистом.
MacIn
15.01.2016 18:00Между пользователем и собственно программистом вы забываете постановщиков задачи, проектировщиков, архитекторов. Это их дело — выяснить, что пользователю нужно, и переложить в ТЗ для программиста.
rg_software
15.01.2016 23:26+2Не согласен.
Проблема в том, что пользователь действительно должен, но не может описать свою проблему с достаточным количеством деталей для её решения.
Вы по сути повторяете вот эту старую мысль: проблема именно в сложности языков вроде Java или Pascal или C, а вот если бы пользователю дали писать на условном русском языке или рисовать диаграммы, то сразу бы рожь заколосилась и наступило бы всеобщее счастье.
В реальности (по крайней мере, так я понимаю из литературы и личного опыта) язык — это уже самый распоследний бастион. Человек неподготовленный в принципе мыслит неформально. Он может сказать, например, что ему нужно определить существование заданной карточки вида «Имя Фамилия» в базе данных. А когда даёшь ему то, что требовалось, оказывается, что «Нефёдов» и «Нефедов» нужно рассматривать как одно и то же, или что одинаковы строки «Иван Иванов» и «Иван <четыре пробела> Иванов», или «Иван» и «иван» — короче говоря, масса тонкостей, о которых люди не думают.
И не стоит ожидать, что люди будут аккуратны и строги в постановке задач. Человек, по складу ума склонный а такого рода рассуждениям, без проблем освоит на базовом уровне любой существующий несложный язык программирования.
MacIn
11.01.2016 18:03Современные языки позволяют многое из того, что показывает автор, записать понятнее и очевиднее.
Просто потому что автор избрал не ту тактику :) Он вместо того, чтобы показать задачи, под кооторые Пролог «заточен», показывает решение привычных в императивной парадигме вещей. Но посколькку они императивны, то в Прологе выглядят плохо. Только и всего.lair
11.01.2016 18:04Тут немедленно возникает разумный вопрос: под какие же задачи пролог заточен?
MacIn
11.01.2016 18:06+2На мой взгляд — проверка набора фактов. Это его основа. Вы можете сделать это императивно, но на Прологе это выглядит еще и понятно.
FiresShadow
11.01.2016 18:26+1Тут немедленно возникает разумный вопрос: под какие же задачи пролог заточен?
Вот этот комментарий частично отвечает на ваш вопрос. Пример автоматического доказательства теоремы можете посмотреть в этой статье, я тоже на эту тему писал.
N_Ikhsanov
11.01.2016 19:01В ПРОЛОГе точно также можно заготовить всякие типовые процедуры, а затем вызывать их в две строки.
ПРОЛОГ предназначен для работы с символьной информацией и для решения задач, требующих логического вывода.
Заготовить процедуры на все случаи жизни — это делает любой программист и не надо называть это «заточенностью.»MacIn
11.01.2016 20:41+1Заготовленные процедуры здесь ни при чем. Пролог позволяет нам проверить набор фактов — это его задача, для этого язык создан. То, что мы можем, используя императивную сущность пролог-машины и отсечение, делать императивные вещи — отдельный разговор.
Вы можете написать на html/css (чисто декларативном языке) игру, которая будет интерактивной. Потому что браузер, интерпретирующий html и css, императивен внутри. Это не отменяет того, что html — язык разметки, он под это «заточен».
MacIn
11.01.2016 18:01А еще в ней, на самом деле, происходит последовательное присвоение (не уверен, что это правильный термин, правда) результатов предикатов для их использования в следующем предикате
Это издержки императивности реализации. Правильный термин — подстановка ЕМНИП.N_Ikhsanov
11.01.2016 20:11Правильный термин — унификация, которая по своей природе является подстановкой аналогично тому как подставляются ссылочные фактические параметры при вызове процедуры.
retran
11.01.2016 15:30+1Зачем описывать Пролог с процедурной точки зрения, если у него совершенно другая вычислительная модель и он решает совершенно другие задачи? Тем более, что сам язык объективно мертв по очень простой причине — реализовать прологовскую модель вычислений (а точнее целое семейство алгоритмов родившихся из нее) под конкретную задачу на любом современном императивном/функциональном проще чем пытаться родить на прологе то для чего он не предназначен.
N_Ikhsanov
11.01.2016 15:48Во всех учебниках ПРОЛОГ описывается с двух точек зрения — декларативной парадигмы и процедурной.
Но декларативная (или логическая) семантика не полностью реализована в языке и только затуманивает понимание языка, если мы хотим на нем делать реальные программы.retran
11.01.2016 15:55+2Правильно. Но пролог ценен только своей декларативной частью. Все остальное — набор ужасных и устаревших костылей нужных как раз для того, чтобы на прологе можно было решать не только узкий набор задач. Вопрос остается тот же — зачем мучатся с прологовскими костылями, если ценная его часть элементарно реализуется под конкретную задачу в любом современном языке?
N_Ikhsanov
11.01.2016 19:06-1Если куски ПРОЛОГа вставляют в эти необъятные монстры — нагромождения бесконечного количества библиотек, называемые современными языками программирования, то чем тут гордиться?
Вам показывают суть парадигмы логического программирования, а стандартные процедуры есть в каждой реализации — можете на них посмотреть, если хочется.retran
12.01.2016 13:35Причем здесь стандартные процедуры?
Плохо понимаю причем тут нагромождения библиотек и как они связаны с языками программирования.
Суть мне показывать не надо, я когда-то изучал пролог, переизобрел его когда игрался с написанием парсера команд на «естественном языке», написал еще один логический движок для решения задачи планирования действий агентов (потому что типовой прологовский «решатель» задачу не решал в силу определенных ограничений). Тем более что вы почему-то показываете логическую парадигму на чисто функциональной задаче от чего у людей и взрывается мозг.N_Ikhsanov
12.01.2016 15:33Синтаксический анализ — функциональная задача?
retran
12.01.2016 15:51+1Если понимать под синтаксическим анализом рекурсивный обход дерева — то да. Заставлять пролог выполнять тот же обход через решение графа отношений — не очень хорошая идея.
N_Ikhsanov
12.01.2016 15:56Разве ПРОЛОГ делает это неэффективно? Там простой перебор вариантов.
retran
12.01.2016 16:01Вопрос не в эффективности, а в понятности, наглядности и той самой декларативности кода.
lair
12.01.2016 16:05Вообще, «простой перебор вариантов» — далеко не всегда самый эффективный способ решения задачи.
rg_software
12.01.2016 01:28Как-то вы себе противоречите. В комментах к первой части это же ваша реплика: «Пора уже понять, что процедурный подход в программировании — временное явление. Все модные языки — это сдача позиций перед неизбежностью декларативности символьных структур.»
И я как раз соглашусь с новым комментарием — в том и беда, что декларативность в Прологе только декларируется (каламбур-с), а на самом деле приходится всё равно размышлять над тем, как машинка под капотом все эти факты и правила будет обрабатывать. То есть скрытая императивность.
По мне так явное лучше скрытого. Явная императивность лучше скрытой императивности. Явную декларативность будем обсуждать тогда, когда она появится.MacIn
12.01.2016 02:24То есть скрытая императивность.
В случае, если мы пишем что-то императивное и пользуемся императивностью самой Пролог-машины. Т.е. используем инструмент в чужой среде.rg_software
12.01.2016 05:29+1Я немного о другом. Декларативность — это когда (как нам пытаются объяснить книжки по декларативному программированию) мы описываем какие-то соотношения между элементами предметной области, а компьютер «сам» выводит ответы на интересующие нас запросы.
В действительности же чистой декларативности нет нигде, потому что две одинаковые (декларативно) программы могут в реальности вести себя совершенно по-разному. Это, собственно, во всех нормальных учебниках сообщается открытым текстом.
Вопрос в том, насколько такая декларативность лучше императивности. Для этого инструмента в принципе не существует «своей» среды, потому что если я просто буду представлять в голове соотношения между символами и записывать их в виде логических формул, ничего хорошего не выйдет: я именно что обязан думать о том, как все эти соотношения будет императивно обрабатывать компьютер. То есть я бы и рад не использовать инструмент в чужой среде, но меня заставляют.MacIn
12.01.2016 14:59В действительности же чистой декларативности нет нигде, потому что две одинаковые (декларативно) программы могут в реальности вести себя совершенно по-разному
Нет, вы не о другом, а ровно о том же: декларативную программу выполняет императивная машина. Начиная от пролог-машины в данном случае и заканчивая собственно ЦП. Чисто декларативными с этой точки зрения можно условно назвать аналоговые компьютеры, в которых задается перемычками схема.rg_software
12.01.2016 20:31+2Нет, я всё-таки о другом. Мне абсолютно всё равно, какое оборудование выполняет мою программу, речь именно о том, что мне как программисту приходится об этом думать.
Если же вы хотите сказать, что декларативное программирование доступно на декларативной машине, императивное — на императивной, а макаронное — на макаронной, что ж, трудно не согласиться, но это же пустой разговор. Других машин в обозримом будущем не предвидится.MacIn
12.01.2016 22:52речь именно о том, что мне как программисту приходится об этом думать.
Это просто данность. Такие у нас машины, независимо от того, хотим мы это учитывать, или нет.
michael_vostrikov
13.01.2016 07:32Обработка данных не происходит мгновенно. Между вводом данных и выводом результата всегда проходит какое-то время.
Обработка данных не происходит сама по себе. Для получения результата из входных данных нужно совершить некоторые действия.
Соответственно, некоторые действия будут выполнены раньше по времени, некоторые позже.
Описание может быть декларативным, выполнение — нет.
В аналоговых устройствах тоже есть задержка на распространение и преобразование сигнала. Если взять 2 рации, то звук из второй появится не одновременно с тем, как мы скажем что-то в первую. При проектировании устройств учитывается направление и движение тока.MacIn
13.01.2016 11:27А дождь падает с неба на землю. К чему трюизмы?
Про распространение сигнала — вы немного уходите не в ту степь. Да, задержка есть, но это не сравнимо с итеративным подходом. Для выполнения двух действий на итеративной машине мы всегда сначала выполним одно, затем другое. То, что они могут занимать разное время — про это я не говорю. В аналоговой системе если вы подключите к источнику сигнала 2 одинаковых блока (не реальных, а идеалистичных), то сигнал до них дойдет одновременно и обрабатывать они его будут одновременно. Это не отменяет задержек, но я по-русски написал «условно». Можно было говорить о многоядерной системе и параллельных вычислениях, или просто комбинационных схемах.
N_Ikhsanov
12.01.2016 07:40Зачем эти спекуляции на тему декларативности? Если Вы внимательно прочитали текст — моя цель показать две вещи:
— в ПРОЛОГе нет ничего сверхсложного и всякий программист может его применять:
— есть задачи задачи, которые на ПРОЛОГе делать лучше, чем в императивном языке.
N_Ikhsanov
12.01.2016 07:34Я написал лозунг, тенденцию развития. Конечно, нынешний ПРОЛОГ не обеспечивает декларативность и его логическая семантика работает только на простых примерах.
Как раз я показываю его явную императивность.
То, что во все модные языки вставляют средства работы со списками, говорит о неизбежности декларативного подхода, а не то что сейчас он доступен.
Появление языка Хаскел демонстрирует стремление обеспечить хотя бы декларативность на уровне статического контроля типов.rg_software
12.01.2016 08:50По правде говоря, я не вижу тенденции. Как некий способ программирования — конечно, да, а откуда тенденция?
Также я не вижу связи между списками и декларативным подходом. Список — это просто структура данных, такая же как массив или стек. Что в ней декларативного?N_Ikhsanov
12.01.2016 09:29Тенденция, конечно не так заметна на отрезке за 10 лет, надо смотреть дальше. То что корпорации выбрасывают на рынок все новые и новые специализированные инструменты — это стремление решить принципиальные проблемы программирования экстенсивным путем.
По поводу декларативности списка — это конечно неявно, список — более абстрактное понятие, чем массив или файл, поскольку его размерность и структура не регламентируется, а рекурсивность понятия список тоже более абстрактная, чем массив.
Вряд ли Вы со мной согласитесь — это мои оценки.rg_software
12.01.2016 20:55Даже если я не соглашусь, мне интересно, как вы выводите эту тенденцию. Скажем, в C++ списки существуют со стандарта 1998 года официально, в Java примерно с того же времени. Специализированные инструменты тоже выбрасывались столько, сколько себя помню (ещё в вузе делали курсовики с помощью Silverrun — это CASE средство для разработки баз данных, тогда была особенно модная тема).
lair
12.01.2016 11:57+2То, что во все модные языки вставляют средства работы со списками, говорит о неизбежности декларативного подхода
Нет, это говорит об удобстве функциональной парадигмы. Нет никакой связи между (функциональными) списками и декларативностью.
(не говоря уже о том, что неплохо бы подтвердить утверждение «во все модные языки вставляют средства работы со списками» — лично я ему не верю, например)N_Ikhsanov
12.01.2016 15:31Может что-нибудь конструктивное скажете?
lair
12.01.2016 15:34+2Консктруктивность критики ложного утверждения состоит в девальвации этого утверждения.
norguhtar
11.01.2016 12:58Он вполне понятен, но надо погрузиться в тему. Плюс там та же проблема что и с функциональными языками, придется вывернуть мышление.
N_Ikhsanov
11.01.2016 13:33Попробуйте почитать ч.1.
Конечно, еще лучше было бы с интерпретатором ПРОЛОГа сразу прогонять примеры.norguhtar
11.01.2016 13:39Мне то зачем? У нас целый курс в универе по нему был.
N_Ikhsanov
11.01.2016 13:45Чтобы не выворачивать мышление.
norguhtar
11.01.2016 13:51В любом случае надо, так-как к императивному подходу, подход пролога никакого отношения не имеет.
N_Ikhsanov
11.01.2016 14:05А вот мне интересно, в каких случаях Вы сталкиваетесь с необходимостью обработки символьной информации в своей программе? Разве символьная обработка не будет у Вас отдельным набором модулей?
MacIn
11.01.2016 18:04Придется. Как-то Хорновские дизъюнкты и метод резолюций не очень похожи на привычное императивное программирование. Хотя они красивы.
begemot_sun
11.01.2016 15:01> В третьей части рассмотрим применение Пролога для построения одного типа интеллектуальных систем — экспертных систем.
Ждем описание методов редукции дерева решений.
evocatus
11.01.2016 17:08+1А я хочу сказать Вам спасибо за Ваши статьи — они меня сподвигли начать изучать Prolog (что было в моём списке дел уже полгода). Читая руководство по языку никак не мог понять вообще о чём речь (какие-то предикаты, атомы, арность). Поискал в Google «Prolog tutorial» и уже смог сделать простейшую программу.
Язык очень интересный, на первый взгляд человека с плохой математической подготовкой напоминает навороченную булеву алгебру. Буду копать дальше, возможно, применю его где-то на практике.
P.S. Если вы пытаетесь писать код в интерпретаторе и всё время получаете «uncaught exception: error...», то сохраните ваш код в файл, а затем импортируйте его командой consult или вот так: [«1.pl»].
Насколько я понял, ошибка выскакивает (в GNU Prolog, yap и SWI Prolog) из-за того, что в интерпретаторе нельзя объявлять свои предикаты (что лично мне кажется неудобным маразмом).MacIn
11.01.2016 18:06никак не мог понять вообще о чём речь (какие-то предикаты, атомы, арность
Ищите «дизъюнкт Хорна» и «метод резолюций».
напоминает навороченную булеву алгебру
Ну… какбэ это оно и есть. Точнее, можно так условно сказать.N_Ikhsanov
11.01.2016 19:15Не надо ни Хорна ни дизъюнктов! Специально написал эти статьи, чтобы показать, как работать с ПРОЛОГом без всей этой математической терминологии. Она нужна теоретикам, программистам она никак не помогает!
MacIn
11.01.2016 20:25+1Надо Хорна и метод резолюций. Без этого не понять, что это за язык и как он работает.
Специально написал эти статьи, чтобы показать, как работать с ПРОЛОГом без всей этой математической терминологии
Вы же видите — это только порождает вопросы«на фига козе баян»зачем нужен Пролог. Если бы вы начали с теории, было бы ясно.
Она нужна теоретикам, программистам она никак не помогает!
Глупость. Это элементарные понятия, и не зная их, в Пролог соваться незачем.N_Ikhsanov
11.01.2016 20:29Всякое утверждение должно быть обосновано. Свое утверждение о ненужности дизъюнктов и понятия клауз Хорна я обосновываю этой публикацией.
А Вы чем обосновываете свое утверждение? Тем что все учебники по ПРОЛОГу забиты этой терминологией, не так ли?MacIn
11.01.2016 20:34+3Свое утверждение о ненужности дизъюнктов и понятия клауз Хорна я обосновываю этой публикацией.
Нет, эта публикация не является обоснованием ненужности теории.
Ваша публикация показывает, как делать на Прологе императивные штуки. Только и всего. И сразу возникают вопросы — посмотрите сами — а зачем извращаться, если можно императивные вещи делать на императивном языке. Вы не разъясняете, чем крут язык, и насколько он прост, вы запутываете.
А Вы чем обосновываете свое утверждение? Тем что все учебники по ПРОЛОГу забиты этой терминологией, не так ли?
Нет. Тем, что понимая принцип резолюций и дизъюнктов Хорна, суть Пролога объясняется в 2 строчки. Без кучи статей.
Потом 1 пример решения простой задачи и человек все понял.
Мое утверждение основано на личном опыте разъяснения другим, что такое Пролог.N_Ikhsanov
11.01.2016 20:42А мое утверждение основано на десятилетнем опыте обучения студентов мехмата, которые на этом ПРОЛОГе делали курсовые и дипломные работы.
MacIn
11.01.2016 20:50+1Вы же беретесь объяснить программистам?
Возможно, эти же работы были бы более элегантными на других языках. Особенно, если это программы расчетов.N_Ikhsanov
11.01.2016 22:45Какие расчеты? Пролог — для символьных вычислений, не для численных!
MacIn
11.01.2016 22:54Я говорю «особенно». Можно сделать что угодно, но есть вещи, для которых Пролог не подходит.
N_Ikhsanov
11.01.2016 19:12А в понимании языка эти две статьи Вам не помогли? Задайте конкретные вопросы.
В режиме интерпретации можно определять предикаты, загружая в память командой assert/1, но правильнее, конечно, записывать их в текстовый файл, потом делать reconsult.MacIn
11.01.2016 20:31В сто раз больше, чем обе статьи, сделает один крохотный пример решения логической задачи на Прологе. Когда нам в свое время читали курс по Прологу, после изучения мат. теории и одного примера решения логической головоломки, все было понятно.
N_Ikhsanov
11.01.2016 20:43В первой части как раз и приведен пример работающей программы решения головоломок.
evocatus
11.01.2016 21:52Я перестал понимать Вашу статью вот в этом месте
а в третьем как [H|T] – произвольный непустой список.
И до сих пор не понимаю как [H|T] может быть связано с произвольным непустым списком. Я до списков ещё не дошёл.
Честно говоря, для меня кривая вхождения Вашей статьи оказалась слишком крутой.
Когда начал смотреть самые простые туториалы, то понял, что Prolog просто инопланетный ЯП для человека с традиционным бэкграундом. Здесь всё настолько по-другому, что я не могу сравнивать Prolog ни с одним ЯП, о которых я хоть что-то знаю (а это десятка 2, не меньше).
Попробую перечислить свои основные открытия на данный момент.
1) Если имя начинается с большой буквы, то это переменная, если с маленькой, то это атом. Такое жёсткое вмешательство в именование очень непривычно.
2) Точка обозначает конец правила
3) Если описываем комплексное правило, состоящее из нескольких предикатов, то разделение их запятой играет роль логического И, а точкой — логического ИЛИ.
4) Перемена мест параметров в факте имеет значениеN_Ikhsanov
11.01.2016 22:43В начале есть ключевая фраза: «Представьте программу, которая состоит только из вызовов процедур, а каждая процедура имеет несколько реализаций»
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
применим для любых непустых списков.
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 — на хвост этого списка.
ForNeVeR
12.01.2016 08:04Пожалуйста, пишите ещё статьи про Пролог. Язык этот очень интересный (и нестандартный для программиста, который в работе чаще сталкивается с мейнстримными языками), и с этим связано неоднозначное отношение сообщества к вашим публикациям (и — особенно — комментариям).
Пишите ещё, многим такие вещи очень интересны!N_Ikhsanov
12.01.2016 08:57+1Я буду продолжать. Следующая статья будет о применении ПРОЛОГа в искусственном интеллекте.
А Вы задавайте больше конкретных вопросов по тексту, пожалуйста.
Очень много любителей глобальных тем.a_bakshaev
15.01.2016 02:56Напишите, если не сложно конкретный сеанс работы. Как скачать, установить, скомпилировать, запустить простейшую программу уровня hello,world на prolog.
N_Ikhsanov
15.01.2016 15:45Мне конечно, несложно это сделать. Но я сомневаюсь в актуальности такой темы, поскольку у каждой системы реализации языка ПРОЛОГ есть свои специфические особенности, и есть своя документация.
Если есть еще желающие, могу написать такую заметку.a_bakshaev
15.01.2016 23:27Хорошо, и статья про экспертную систему это отлично. Еще бы с обзором, что может экспертная система. К примеру, интересует задача автоматической SSE векторизации С кода. Интерфейса тут минимум, и как раз символьные данные на входе и выходе.
norguhtar
У пролога есть одна довольно значимая проблема. Сделать на нем какой либо интерфейс взаимодействия с пользователем тот еще цирк. Как итог его неплохо бы использовать на манер lua т.е. исключительно как встраиваемый язык. Я с ним имел дело довольно таки давно, как с этим сейчас?
N_Ikhsanov
В разных реализациях языка есть свои средства — встроенные предикаты для взаимодействия с внешней средой.
Можно сделать и по другому — сделать на ПРОЛОГе бbблиотеку или DLL.
norguhtar
Опять же получается в лыжах на асфальте. В этом и проблема. А на чистом прологе писать ПО мягко говоря не очень. Это кстати в свое время сильно ограничивало использование как его так и лиспа.
N_Ikhsanov
ПРОЛОГ — язык специализированный — для задач обработки символьной информации и искусственного интеллекта.
norguhtar
Иии? Что толку от него если я им не могу воспользоваться, а средства ввода-вывода убоги до нельзя?
N_Ikhsanov
Разве Вы в своей работе пользуетесь только одним языком программирования?
norguhtar
Внимательно читаем первый коммент. Я указываю проблему Prolog это интеграция. Из-за этого его использование в том числе в полезных целях весьма затруднено.
N_Ikhsanov
Может быть Вы не смотрели профессиональный ПРОЛОГ? Не вижу проблем с интеграцией.
norguhtar
Эм, а что такое профессиональный ПРОЛОГ? :)
N_Ikhsanov
Который продается, например SICStus Prolog.
FiresShadow
Не занимался интеграцией с прологом, но первое, что приходит в голову — это передача итоговых данных по TCP на localhost. И это не единственный способ.
N_Ikhsanov
Во всех реализациях ПРОЛОГа большое внимание уделяется средствам интеграции, есть несколько вариантов.
Например, в ARITY/PROLOG есть просто встроенный Си, не говоря уже о том, что можно компилировать ПРОЛОГ в obj — файл, DLL или EXE.
norguhtar
Это довольно заморочно. И в случае встроенный C это выворачивание на изнанку. По мне хорошо именно бы сделать наоборот, когда у меня есть императивный язык который используется для GUI и прочих абстракций, а когда мне нужнен prolog то я его вызываю оттуда. Все можно компилировать пролог в ddl или exe это сложный путь.
N_Ikhsanov
На Прологе делаете DLL и вызываете из своей «обычной» программы.
norguhtar
Ну опять же tcp для пролога тоже не очень нативный метод :)
N_Ikhsanov
Если хочется, можно на ПРОЛОГе сделать EXE и вызывать его через средства управления процессами.
Danov
Метафорой отвечу: А как на языке мат алгебры можно ввод вывод писать???
Языки предназначены для описания ограниченной предметной области.
FiresShadow
Есть Visual Prolog. Например, button::defaultAction :- Name = idc_ancestordialog_personname:getText(). В логической парадигме программирования нет присвоения переменных и вызовов функций, поэтому концептуально это можно перевести как «defaultAction истинно, когда Name совпадает с тем, что ввёл пользователь в окошко». Потом в каком-то месте программы утверждается, что defaultAction истинно (напомню, вызовов функций нет), и Prolog начинает искать все Name, которые совпадают с тем, что ввёл пользователь. Для этого у пользователя просят ввести данные. После этого считается, что Name определён. В общем-то да, GUI в логической парадигме немного запутаннее, чем в функциональной или объектно-ориентированной, но тем не менее, вполне осуществим.
norguhtar
Как итог имеем отдельную прослойку которая по сути императивна :) Я по этому и говорю, что более правильно prolog бы в виде встраиваемого языка использовать аля lua. В этом случае такой проблемы как работа с GUI нет :)
FiresShadow
Ну да, пролог не для GUI. В программе, написанной на языке логического программирования, решение задачи часто ищется посредством хорошо оптимизированного брутфорса. Изначально это задумывалось как перебор математических фактов с целью доказательства теорем. Можно создать обычную прикладную программу на прологе, и она будет гораздо гибче и понятнее, чем программа из любой другой парадигмы, но будет она очень тормознутой — ибо брутфорс. Можно написать на прологе и быструю программу, но тогда нужно будет не просто описывать известные факты о системе, а как бы направлять ход выполнения программы, по сути возвращаясь к императивному программированию. Логическое программирование тем и ценно, что программист, например, описывает что такое ток, проводочки и диоды, задаёт максимальное количество элементов на микросхеме, и просит программу собрать ему синхрофазотрон. Без шуток, этот очень оптимизированный брутфорс творит чудеса, и Ватсон свидетель чудес его!