Пролог является естественным языком для построения экспертных систем. Существуют различные подходы к программированию экспертных систем. Опишем простой и элегантный метод создания оболочки экспертных систем на базе метаинтерпретатора Пролога.
Метапрограммы обращаются с другими программами как с данными; они выполняют их анализ, преобразование и моделирование. Легкость разработки метапрограмм, или метапрограммирования, на Прологе обусловлена эквивалентностью программ и данных - и те и другие являются термами Пролога. Мы рассматриваем метапрограммы особого класса - метаинтерпретаторы.
Метаинтерпретатор для некоторого языка - это интерпретатор для языка, написанный на том же самом языке. Некоторые языки программирования, такие как Лисп и Пролог, позволяют легко разрабатывать метаинтерпретаторы, что является важным свойством таких языков. Оно делает возможным построение интегрированной среды программирования и обеспечивает доступ к вычислительным средствам языка.
Опишем простой интерпретатор для чистого Пролога. Отношение solve(Goal) истинно, если цель Goal истинна в отношение программы, подлежащей интерпретации.
% программа 1
solve(true):-!. % поскольку clause(true,true) - также истина
solve((A,B)):-
solve(A),
solve(B).
solve(A):-
clause(A,B),
solve(B).
Интерпретатору соответствует следующее декларативное толкование. Константа true является истинной. Конъюнкция (A,B) истинна, если истинна цель A и истинна цель B. Цель A истинна, если в интерпретируемой программе существует предложение A :- B , такое, что цель B истинна.
Теперь дадим процедурное толкование этим трем предложениям программы 1. Факт solve устанавливает, что пустая цель, представленная в Прологе атомом true, достижима. Следующее предложение относится к конъюнктивным целям. Оно читается так: “Для достижения конъюнкции целей (A,B) необходимо достичь цели A и цели B”. Общий случай редукции цели покрывается последним предложением программы. Чтобы доказать некоторую цель, из программы выбирается предложение, заголовок которого унифицируется с целью, а затем рекурсивно применяется тело предложения.
Процедурное толкование предложений Пролога необходимо, чтобы показать, что метаинтерпретатор, представленный программой 1, действительно отражает возможности Пролога при реализации абстрактной вычислительной модели логического программирования. Такими возможностями, например являются выбор для редукции крайней левой цели и использование последовательного поиска с возвратом при выборе предложения для редукции цели. Порядок целей в теле предложения solve, содержащего конъюнктивные цели, гарантирует, что самая левая цель в конъюнкции решается первой. Последовательный поиск и возврат осуществляются при доказательстве цели clause согласно принципам выполнения Пролог-программ.
Постоянная работа интерпретатора обеспечивается третьим предложением программы 1. При вызове предложения clause выполняется унификация с заголовками предложений, имеющихся в программе. Это предложение ответственно также за получение различных решений при возвратах. Кроме того, возвраты происходят и в конъюнктивном правиле (возвраты от цели B к цели A).
Поучителен разбор протокола работы метаинтерпретатора, представленного программой 1, при доказательстве некоторой цели.
Определение предиката member:
?- listing(member).
member(A, [A|B]).
member(A, [B|C]) :-
member(A, C).
Интерпретация предиката member:
?- solve(member(X,[a,b,c])).
X = a ;
X = b ;
X = c ;
No
Включение трассировки для solve:
?- trace(solve,[+call,+exit]).
solve/1: call exit
Yes
Трассировка (частичная) solve:
?- solve(member(X,[a,b,c])).
T Call: ( 6) solve(member(G988, [a,b,c]))
T Call: ( 7) solve(true)
T Exit: ( 7) solve(true)
T Exit: ( 6) solve(member(a, [a,b,c]))
X = a ;
T Call: ( 7) solve(member(G988, [b,c]))
T Call: ( 8) solve(true)
T Exit: ( 8) solve(true)
T Exit: ( 7) solve(member(b, [b,c]))
T Exit: ( 6) solve(member(b, [a,b,c]))
X = b ;
T Call: ( 8) solve(member(G988, [c]))
T Call: ( 9) solve(true)
T Exit: ( 9) solve(true)
T Exit: ( 8) solve(member(c, [c]))
T Exit: ( 7) solve(member(c, [b,c]))
T Exit: ( 6) solve(member(c, [a,b,c]))
X = c ;
T Call: ( 9) solve(member(G988, []))
No
|