Использование PIE в Visual Prolog
В настоящей главе рассматриваются основные отличия языка Visual Prolog от реализаций языка Пролог, удовлетворяющих стандарту (т. н. стандартного Пролога), связанные с унификацией термов. Кроме этого, приводится пример использования интерпретатора PIE в программе на языке Visual Prolog.
16.1. Унификация термов в стандартном Прологе
В языке Visual Prolog процедура унификации термов имеет некоторые ограничения. В частности, напомним, что при унификации термов с помощью знака равенства все свободные переменные должны стать конкретизированными. В стандартном Прологе по окончании вычислений переменные могут оставаться свободными.
Например, в PIE ответ на запрос программы
less(0, _). ?- less(X, Y).
выглядит следующим образом:
X = 0, Y = _.
(в системе Visual Prolog в этом случае возбуждается исключение).
Списки в стандартном Прологе могут содержать элементы разных доменов. Например, цель
?- L = [_, _], L = [_, [2]], L = [1, _ | _].
имеет решение
L = [1, [2]].
Факты могут описывать данные с неполной информацией. Например, программа может иметь вид (см. листинг 2.3):
library(magazine("Компьютерра", 2, 2009)).
library(book(author("Чехов", _, _), "Избранное", _)).
library(book(author("Великова", "Людмила", "Викторовна"),
"Русский язык", edition("Москва", "МЦНМО", 2003))).
?- library(book(author(X, _, _), Y, edition(_, _, 2003))).
Приведенная в программе цель имеет два решения:
X = "Чехов", Y = "Избранное"; X = "Великова", Y = "Русский язык".
В стандартном Прологе имеются предикаты для конструирования и анализа сложных термов arg/3 и functor/3. Аргументы этих предикатов имеют следующий смысл: arg(N, Term, Argument) и functor(Term, Functor, Arity), где N — позиция аргумента Argument в терме Term (позиции нумеруются с единицы), а Functor — это функтор арности Arity терма Term. Например,
?- arg(2, b(_, "Чайка", _), Y).
Y = "Чайка".
?- functor(T, b, 3), arg(1, T, "Чехов"), arg(3, T, ed("СПб", _, 2014)).
T = b("Чехов", _, ed("СПб", _, 2014)).
В стандартном Прологе станут более простыми и некоторые другие программы, рассмотренные ранее. Например, будет короче решение задачи "Кино" (см. п. 3.5). Из программы, приведенной в листинге 3.1, можно удалить предикат indicator, так что она примет вид:
proposition(1, A, B):- A = 1, B = 1; A = 0.
proposition(2, V, G):- V = 1; G = 1.
proposition(3, B, D):- B = 1, D = 0; B = 0, D = 1.
proposition(4, V, D):- V = 1, D = 1; V = 0, D = 0.
solution(A, B, V, G, D):-
proposition(1, A, B), proposition(2, V, G),
proposition(3, B, D), proposition(4, V, D),
proposition(1, G, A), proposition(1, G, V).
?- solution(Anna, Boris, Victor, Gregory, Darya).
Можно использовать разностные списки (ниже используется префиксное обозначение):
conc(d(A, B), d(B, C), d(A, C)). ?- conc(d([1, 2, 3 | X], X), d([a, b], []), d(L, [])). X = [a, b], L = [1, 2, 3, a, b].
Предикаты обработки списков length, member и append можно использовать для конструирования списков:
length([], 0).
length([_ | T], N):- length(T, K), N is K + 1.
member(H, [H | _]).
member(H, [_ | T]):- member(H, T).
append([], L, L).
append([H | T], L, [H | R]):- append(T, L, R).
?- length(L, 2), !.
L = [_, _].
?- L = [_, _], member(1, L), member(2, L).
L = [1, 2].
L = [2, 1].
?- append([_], [1, 2], L).
L = [_, 1, 2].
Решение головоломки Эйнштейна в стандартном Прологе выглядит совсем просто.
right(A, B, [A, B | _]).
right(A, B, [_ | L]):- right(A, B, L).
next(A, B, Houses):- right(A, B, Houses).
next(A, B, Houses):- right(B, A, Houses).
member(H, [H | _]).
member(H, [_ | T]):- member(H, T).
solve(Houses):-
Houses = [[_, _, _, _, _], [_, _, _, _, _], [_, _, _, _, _],
[_, _, _, _, _], [_, _, _, _, _]],
member(["red", "Englishman" | _], Houses),
member([_, "Spaniard", "dog" | _], Houses),
member(["green", _, _, "coffee" | _], Houses),
member([_, "Ukrainian", _, "tea" | _], Houses),
right(["ivory" | _], ["green" | _], Houses),
member([_, _, "snails", _, "Winston"], Houses),
member(["yellow", _, _, _, "Kool"], Houses),
Houses = [_, _, [_, _, _, "milk" | _] | _],
Houses = [[_, "Norwegian" | _] | _],
next([_, _, _, _, "Chesterfield"], [_, _, "fox" | _], Houses),
next([_, _, _, _, "Kool"], [_, _, "horse" | _], Houses),
member([_, _, _, "orange juice", "Lucky Strike"], Houses),
member([_, "Japanese", _, _, "Parliament"], Houses),
next([_, "Norwegian" | _], ["blue" | _], Houses),
member([_, _, "zebra" | _], Houses),
member([_, _, _, "water" | _], Houses).
?- solve(Houses).
Пример
16.1.
Головоломка Эйнштейна в PIE
16.2. Применение интерпретатора в компиляторе
Некоторые программы в стандартном Прологе выглядят короче и изящнее, чем программы на языке Visual Prolog. Однако следует отметить, что, во-первых, программы, решающие те же самые задачи в Visual Prolog работают существенно быстрее, и, во-вторых, что чем сложнее решается задача, тем короче ее решение, вероятнее всего, будет именно на языке Visual Prolog.
Ниже приводится пример использования интерпретатора PIE в программе на языке Visual Prolog. В отличие от языка Visual Prolog, в реализациях, удовлетворяющих стандарту, предикат assert используется для добавления не только фактов, но и правил. В приведенной ниже программе в базе данных хранятся примеры небольших программ вместе с примерами запросов. Правила добавляются в PIE динамически (учитывается порядок перебора фактов). Ответы на запросы находит интерпретатор. Результаты выводятся в окне консоли.
open core, console, pfc\pie
class facts
program: (string Clauses, string Goal).
clauses
program(@"
add(0, X, X).
add(s(X), Y, s(Z)):- add(X, Y, Z).",
"add(A, B, s(s(s(0)))).").
program(@"
f(0, 0).
f(N, s(X)):- f(K, X), N is K + 1.",
"f(2, X), f(3, Y), add(X, Y, Z), !.").
run():-
Pie = pie::new(),
program(Program, Goal),
Pie:assertClauses(Program),
Output = outputStream_string::new(),
Pie:outputStream := Output,
try Pie:run(Goal),
Answer = Output:getString(),
writef("%\n\n?- %\n", Program, Goal),
write(Answer), nl, nl
catch E do
writef(" Error % ...", E), fail
end try,
fail;
_ = readLine().
Пример
16.2.
Пример использования класса pfc\pie
Перед строкой (string) поставлен знак @. Благодаря этому для нее сохраняется форматирование со страницы кода (см. определение предиката program/2).
Упражнения
- Определите операцию линеаризации списка в PIE (см. листинг 6.9).
- Напишите программу, которая загружает в pie текст программы, хранящейся в текстовом файле, после того, как пользователь введет имя этого файла в окно консоли, и отвечает на запросы, которые пользователь вводит в окне консоли, до тех пор пока он не введет специальную команду.
- Напишите программу, которая ищет в pie ответы на запросы, выполняя "погружение" на заданную глубину. В частности, она не должна входить в бесконечный цикл при вычислении ответа на запрос q(A) к программе "q(X):- q(X). q(3)." (Указание. Используйте в pie предикат clause).
- Определите в программе на языке Visual Prolog предикат, принадлежащий домену invokePredicate или invokeFunction (см. Help). Приведите пример его использования в pie.