Prolog Language
Использование современного пролога
Поиск…
Вступление
Многие современные системы Prolog постоянно развиваются и добавили новые возможности для решения классических недостатков языка. К сожалению, многие учебники Prolog и даже учебные курсы по-прежнему вводят только устаревший пролог. Эта тема призвана проиллюстрировать, как современный Пролог преодолел некоторые проблемы и довольно жестокий синтаксис, который появляется в более раннем Prolog и все еще может быть введен.
CLP (FD) для целочисленной арифметики
Традиционно Prolog выполнял арифметику с использованием операторов is
и =:=
. Однако несколько текущих прологов предлагают CLP (FD) (Constraint Logic Programming over Finite Domains) как более чистую альтернативу для целочисленной арифметики. CLP (FD) основан на сохранении ограничений, которые применяются к целочисленному значению, и их объединению в памяти.
CLP (FD) является расширением в большинстве прологов, которые его поддерживают, поэтому их необходимо загружать явно. После того, как он будет загружен, то #=
синтаксис может занять место как is
и =:=
. Например, в SWI-Prolog:
?- X is 2+2.
X = 4.
?- use_module(library(clpfd)).
?- X #= 2+2.
X = 4.
В отличие is
, #=
способен решать простые уравнения и унифицировать в обоих направлениях:
?- 4 is 2+X.
ERROR: is/2: Arguments are not sufficiently instantiated
?- 4 #= 2+X.
X = 2.
CLP (FD) предоставляет свой собственный синтаксис генератора.
?- between(1,100,X).
X = 1;
X = 2;
X = 3...
?- X in 1..100.
X in 1..100.
Обратите внимание, что генератор фактически не запускается: сохраняется только ограничение диапазона, готовое к последующим связям с ним. Генератор может быть принудительно запущен (и ограничения грубой силы) с использованием предиката label
:
?- X in 1..100, label([X]).
X = 1;
X = 2;
X = 3..
Использование CLP может позволить некоторое умное сокращение случаев грубой силы. Например, используя целочисленную арифметику в старом стиле:
?- trace.
?- between(1,10,X), Y is X+5, Y>10.
...
Exit: (8) 6 is 1+5 ? creep
Call: (8) 6 > 10 ? creep
...
X = 6, Y = 11; ...
Пролог по-прежнему проходит через значения 1-5, хотя математически доказывается из данных условий, что эти значения не могут быть полезны. Использование CLP (FD):
?- X in 1..10, Y #= X+5, Y #> 10.
X is 6..10,
X+5 #= Y,
Y is 11..15.
CLP (FD) немедленно выполняет математику и разрабатывает доступные диапазоны. Добавление label([Y])
приведет к тому, что X будет выполняться только через полезные значения 6..10. В этом примере игрушек это не увеличивает производительность, потому что с таким небольшим диапазоном, как 1-10, обработка алгебры занимает столько же, сколько цикл сделал бы; но когда обрабатывается больший диапазон чисел, это может значительно сократить время вычисления.
Поддержка CLP (FD) является переменной между Prologs. Признанная лучшая разработка CLP (FD) находится в SICStus Prolog, который является коммерческим и дорогим. SWI-Prolog и другие открытые прогоны часто имеют некоторую реализацию. Visual Prolog не включает CLP (FD) в своей стандартной библиотеке, хотя для него доступны библиотеки расширений.
Forall вместо отказоустойчивых циклов
Некоторые «классические» учебники Prolog по-прежнему используют запутанный и подверженный ошибкам синтаксис цикла, основанный на отказе, когда для создания обратной привязки используется команда fail
чтобы применить цель к каждому значению генератора. Например, чтобы напечатать все числа до заданного предела:
fdl(X) :- between(1,X,Y), print(Y), fail.
fdl(_).
Подавляющее большинство современных прологов больше не требуют этого синтаксиса, вместо этого обеспечивая предикат более высокого порядка для решения этой проблемы.
nicer(X) :- forall(between(1,X,Y), print(Y)).
Мало того, что это намного легче читать, но если цель печати , которая могла быть неудачной, использовалась вместо печати , ее отказ был бы правильно обнаружен и передан, тогда как неудачи целей в отказоустойчивом цикле будут сбиты с вынужденным отказом который управляет циклом.
Visual Prolog имеет специальный синтаксический сахар для этих циклов в сочетании с предикатами функций (см. Ниже):
vploop(X) :- foreach Y = std::fromTo(1,X) do
console::write(X)
end foreach.
Хотя это выглядит как императив для цикла, она по- прежнему следует правилам Пролога: в частности, каждая итерация Еогеаспа является его собственной областью.
Предикаты в стиле функции
Традиционно в Prolog «функции» (с одним выходом и связанными входами) были написаны как обычные предикаты:
mangle(X,Y) :- Y is (X*5)+2.
Это может создать трудность, заключающуюся в том, что если предикат функционального стиля вызывается несколько раз, необходимо «временные переменные цепочки».
multimangle(X,Y) :- mangle(X,A), mangle(A,B), mangle(B,Y).
В большинстве Prologs, можно избежать этого, написав альтернативный оператор инфиксного использовать вместо is
который расширяет выражения в том числе альтернативной функции.
% Define the new infix operator
:- op(900, xfy, <-).
% Define our function in terms of the infix operator - note the cut to avoid
% the choice falling through
R <- mangle(X) :- R is (X*5)+2, !.
% To make the new operator compatible with is..
R <- X :-
compound(X), % If the input is a compound/function
X =.. [OP, X2, X3], % Deconstruct it
R2 <- X2, % Recurse to evaluate the arguments
R3 <- X3,
Expr =.. [OP, R2, R3], % Rebuild a compound with the evaluated arguments
R is Expr, % And send it to is
!.
R <- X :- R is X, !. % If it's not a compound, just use is directly
Теперь мы можем написать:
multimangle(X,Y) :- X <- mangle(mangle(mangle(Y))).
Однако некоторые современные Прологи идут дальше и предлагают специальный синтаксис для этого типа предиката. Например, в Visual Prolog:
mangle(X) = Y :- Y = ((X*5)+2).
multimangle(X,Y) :- Y = mangle(mangle(mangle(X))).
Обратите внимание, что предикат <-
operator и предикат функционального стиля выше по-прежнему ведут себя как отношения - для них законно иметь точки выбора и выполнять множественную унификацию. В первом примере мы предотвращаем это с помощью разрезов. В Visual Prolog нормально использовать функциональный синтаксис для отношений, а точки выбора создаются обычным способом - например, цель X = (std::fromTo(1,10))*10
успешно X = (std::fromTo(1,10))*10
с привязками X = 10 , X = 20, X = 30, X = 40 и т.д.
Объявления потока / режима
При программировании в Prolog не всегда возможно или желательно создавать предикаты, которые объединяются для каждой возможной комбинации параметров. Например, предикат between(X,Y,Z)
который выражает, что Z численно находится между X и Y. Он легко реализуется в тех случаях, когда все X, Y и Z связаны (либо Z находится между X и Y, либо это не) или где X и Y связаны и Z свободен (Z унифицирует все числа между X и Y или предикат терпит неудачу, если Y <X); но в других случаях, например, когда X и Z связаны, а Y свободен, потенциально существует бесконечное количество унификаций. Хотя это можно реализовать, этого обычно не будет.
Объявление потока или декларации режима позволяют явное описание поведения предикатов при вызове с различными комбинациями связанных параметров. В случае between
декларацией будет:
%! between(+X,+Y,+Z) is semidet.
%! between(+X,+Y,-Z) is nondet.
Каждая строка указывает один потенциальный шаблон вызова для предиката. Каждый аргумент украшен знаком +
чтобы указать случаи, когда он привязан, или -
указывать случаи, когда он отсутствует (существуют и другие декорации, доступные для более сложных типов, таких как кортежи или списки, которые могут быть частично связаны). Ключевое слово после того , как показывает поведение предиката в этом случае, и может быть один из них:
-
det
если предикат всегда преуспевает без точки выбора. Например,add(+X,+Y,-Z)
являетсяdet
потому что добавление двух заданных чисел X и Y всегда будет иметь ровно один ответ. -
semidet
если предикат либо преуспевает, либо не работает, без выбора. Как и выше,between(+X,+Y,+Z)
являетсяsemidet
потому что Z либо находится между X и Y, либо нет. -
multi
если предикат всегда преуспевает, но может иметь точки выбора (но также может и не быть). Например,factor(+X,-Y)
будетmulti
потому что число всегда имеет по крайней мере один фактор - сам, но может иметь больше. -
nondet
если предикат может преуспеть с точками выбора или сбой. Например,between(+X,+Y,-Z)
nondet
потому что может быть несколько возможных объединений Z для чисел между X и Y, или если Y <X, то между ними нет чисел, и предикат терпит неудачу.
Объявления потока / режима также могут быть объединены с маркировкой аргументов, чтобы уточнить, что означают термины, или с помощью ввода. Например, between(+From:Int, +To:Int, +Mid:Int) is semidet
.
В чистых прологах объявления потока и режима являются необязательными и используются только для генерации документации, но они могут быть чрезвычайно полезны, чтобы помочь программистам определить причину ошибок при создании.
В Mercury декларации потока и режима (и типы) являются обязательными и проверяются компилятором. Используемый синтаксис приведен выше.
В Visual Prolog декларации потока и режима и типы также являются обязательными, а синтаксис отличается. Вышеуказанное заявление будет написано так:
between : (int From, int To, int Mid) determ (i,i,i) nondeterm (i,i,o).
Значение такое же, как и выше, но с различиями, которые:
- Объявления потока / режима отделены от объявлений типов (поскольку предполагается, что поток / режим для одного предиката не будет меняться при перегрузке типа);
-
i
иo
используются для+
и-
и сопоставляются с параметрами, основанными на упорядочении; - Используемые термины различны.
det
становитсяprocedure
,semidet
становитсяdeterm
иnondet
становитсяnondeterm
(multi
ещеmulti
).