Использование 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.