Управление предикатами в Прологе

Есть любопытство, связанное с управлением предикатом Prolog.

Предположительно у меня есть предикат f (A, X) и g (B).

f(A,X):- a,b,c, g(X).
g(B):- true.

a - returns true
b - returns true.
c - returns false.
where a,b and c are random predicates.

Как я могу продолжить вычисление g(X) в предикате f(A,X), если c возвращает false?


person Cristina    schedule 06.11.2010    source источник


Ответы (2)


Если вы намереваетесь определить f(A,X) так, чтобы g(X) нужно было оценивать, является ли c неудачным, то либо:

  1. Вы можете закодировать это, используя импликацию (->) и / или дизъюнкцию (;), или
  2. f(A,X) не нужно определять в терминах c. Это предполагает, что c не имеет побочных эффектов (например, утверждения фактов базы данных с использованием assert или печати ввода-вывода в поток), которые изменяют среду и которые не могут быть отменены при сбое c, и в этом случае первый вариант предпочтительнее.

Есть несколько альтернатив использования дизъюнкции, например:

f(A,X) :- ((a, b, c) ; (a, b)), g(X).

Это определение (см. Выше) вообще не зависит от c, но всегда будет выполняться c (до тех пор, пока a и b будут успешными). Дизъюнкция (;) позволяет PROLOG вернуться назад, чтобы попытаться выполнить a, b снова, если c вообще не удалось, и продолжить на g(X). Обратите внимание, что это эквивалентно:

f(A,X) :- a, b, c, g(X).
f(A,X) :- a, b, g(X).

Чтобы PROLOG не возвращался для оценки f(A,X) дважды из-за второго (идентичного) предиката заголовка f(A,X) для каждой оценки, вы можете разместить вырезку (!), если ваша реализация поддерживает это, сразу после c подзадача в первом пункте. Вырезка помещается после c, потому что мы не хотим, чтобы интерпретатор выполнял этот выбор предложения f(A,X), если c потерпел неудачу, вместо этого мы хотим, чтобы интерпретатор вышел из этого предложения и попробовал следующий, чтобы эффективно игнорировать c и продолжить обработку g(X).

Также обратите внимание, что это решение полагается на a и b, не имеющее побочных эффектов, потому что при сбое c a и b выполняются снова. Если все a, b и c имеют побочные эффекты, вы можете попробовать использовать импликацию:

f(A,X) :- a, b, (c -> g(X) ; g(X)).

Это также будет эффективно всегда выполнять g(X) независимо от c сбоя или нет, и не будет выполнять a и b снова, если c завершится сбоем. Это определение с одним предложением также не оставит точки выбора, как предыдущее предложение.

person Community    schedule 06.11.2010

Думаю, вы могли бы обернуть c в ignore/1. Рассмотрим, например,

?- false, writeln('Hello World!').
false.

?- ignore(false), writeln('Hello World!').
Hello World!
true.

Но зачем вам продолжать, если c терпит неудачу? Какой вариант использования?

Я тестировал этот код в SWI-Prolog, я не уверен, есть ли в других Prologs false/0 и ignore/1. Последнее можно определить так:

ignore(Goal) :- Goal, !.
ignore(_).
person Kaarel    schedule 06.11.2010
comment
+1 за вопрос, почему это было бы полезно. Переносимый предикат отказа - fail, кстати. (Что несложно реализовать: fail :- 0=1.) - person Fred Foo; 09.11.2010
comment
Один из сценариев, в котором вы можете захотеть продолжить, если c завершится неудачей, - это когда c имеет побочные эффекты; см. мой ответ на этот вопрос для более подробной информации. - person ; 09.11.2010