Управление перебором. Отсечение
Для того чтобы сделать поиск решений более эффективным, в язык Пролог были добавлены внелогические средства управления процессом вычислений. Внелогическими называют предикаты, процедурная семантика которых лежит вне рамок SLD-резолютивного вывода. Такими предикатами являются, например, предикаты ввода и вывода. Предикат отрицания также внелогический (см. п. 2.8).
Основными средствами управления перебором являются предикаты отсечения, fail и отрицания. В настоящей главе вводится отсечение. Определяются режимы детерминизма предикатов и потоки параметров. Обсуждается предикат findall, собирающий решения в список, и его обобщение — конструкция […||…].
В главе также рассматриваются примеры решения логических задач. Для решения задач обычно используется метод "образовать и проверить": сначала генерируются возможные значения переменных, а потом проверяется удовлетворение их условиям задачи. В целях сокращения перебора отбрасывание ненужных значений должно производиться как можно раньше. В данном случае перебором управляет порядок следования вычисляемых подцелей.
3.1. Статическое отсечение
Отсечение обозначается знаком "!". Правило с отсечением в общем случае имеет вид:
.
Отсечение используется для предотвращения отката после достижения цели. Пусть цель имеет вид: . Если в правиле достигнуто отсечение, то вычисления не выходят за пределы этого правила, при этом
- откат для подцелей
не производится;
- для подцелей
откат возможен.
Например, пусть программа имеет вид:
цифра(0). цифра(1):- !. цифра(2).
Частная цель
цифра(2).
является успешной. Но общая цель
цифра(X).
имеет всего два решения, так как во втором правиле стоит отсечение:
X = 0 X = 1.
Поэтому третье правило игнорируется. Далее, цель
цифра(X), !, цифра(Y)
также имеет два решения, так как для первой подцели откат невозможен:
X = 0, Y = 0 X = 0, Y = 1
Если в последней цели убрать отсечение, то решений будет четыре. А если его убрать и из программы, то решений будет девять.
Отсечение используется:
- для предотвращения ненужных вычислений;
-
для моделирования ветвления "Q: если A, то B, иначе C":
Q:- A, !, B. Q:- C.
-
для выражения отрицания. Например, правило "P:- not(A)." равносильно совокупности правил:
P:- A, !, fail. P.
Если удаление отсечения не изменяет множество решений, то оно называется зеленым, а если изменяет, то красным (см. листинг 3.4).
3.2. Динамическое отсечение
Отсечение "!" является статическим. В языке Visual Prolog имеется динамическое отсечение, которое предотвращает откат только для некоторых подцелей. Такие подцели помещаются между предикатами programControl::getBackTrack и programControl::cutBackTrack/1. Например, найти мужчин, которые являются родителями, можно следующим образом:
run():- male(X), B = programControl::getBackTrack(), parent(X, _), programControl::cutBackTrack(B), write(X), nl, fail; _ = readLine().
В этом случае имена мужчин, имеющих детей, будут выведены по одному разу. Если убрать динамическое отсечение, то каждое имя будет выведено столько раз, сколько детей этого мужчины известно программе.
Динамическое отсечение всегда можно заменить статическим, с помощью определения дополнительного предиката или предикатов. Например, в данном случае можно ввести предикат проверки, является ли некто родителем:
class predicates isParent: (string) determ. clauses isParent(X):- parent(X, _), !. run():- male(X), isParent(X), write(X), nl, fail; _ = readLine().