Предположим следующую программу:
nat(0).
nat(s(N)) :- nat(N).
/* 0+b=b */
plus(0,B,B) :- nat(B).
/* (a+1)+b = c iff a+(b+1)=c */
plus(s(A),B,C) :- plus(A,s(B),C).
Он отлично подходит для сложения двух чисел, но когда я пытаюсь выполнить запрос следующего вида:
plus(Z,Z,s(0)).
Он переходит к поиску возможных значений для Z
еще долго после того, как должно быть очевидно, что решения нет (т.е. Z>s(0)
)
Я знаком с оператором cut (!
), и моя интуиция подсказывает, что решение имеет к нему какое-то отношение, я просто не знаю, как его использовать в этом случае.
3 ответа
!/0
постоянно не является хорошим решением таких проблем. Обычно это приводит к потере действительных решений для более общих запросов.
Вместо этого рассмотрите возможность использования ограничений конечной области, которые отлично работают в этом случае:
?- use_module(library(clpfd)).
true.
?- Z + Z #= 0.
Z = 0.
?- Z + Z #= 0, Z #> 0.
false.
РЕДАКТИРОВАТЬ : возможное решение без CLP (FD) по запросу:
plus(0, Y, Y).
plus(s(X), Y, s(Z)) :- plus(X, Y, Z).
Итак, вам нужно понять точные свойства завершения конкретной программы. Пролог здесь сильно отличается от других языков программирования, поскольку имеет относительно сложный механизм выполнения. В частности, возврат с возвратом тесно переплетается с «нормальным разрешением», а затем сочетается с унификацией.
Самый простой способ понять проблему исходной программы - рассмотреть следующий фрагмент отказа (см. Ссылку для других Примеры):
plus(0,B,B) :- false, nat(B). plus(s(A),B,C) :- plus(A,s(B),C), false.
Если эта крошечная программа не завершится, то и ваша исходная программа не завершится. Итак, мы можем сначала рассмотреть вопрос, когда эта программа не будет завершена.
Рассмотрим третий аргумент: передается просто переменная C
. Его ценность (в этом фрагменте) никого не интересует. Таким образом, третий аргумент никак не повлияет на расторжение договора!
Хуже того, второй аргумент - это просто свободная переменная B
в заголовке, таким образом, опять же, аргумент никогда не повлияет на завершение этого фрагмента или нет.
Итак: если вы хотите, чтобы этот фрагмент завершился, необходимо знать первый аргумент. В противном случае программа зациклится. Вы также можете использовать логический вывод завершения для , чтобы увидеть, как это становится условием завершения:
plus(A,B,C)terminates_if b(A),b(B);b(A),b(C).
Кажется, лучше всего переформулировать вашу программу. Предоставляя вам следующее условие завершения:
plus(A,B,C)terminates_if b(A),b(B);b(C).
В Prolog мы обычно хотим еще больше обобщать программы, принимая также некоторые неожиданные решения и улучшая условие завершения до еще лучше:
plus(A,B,C)terminates_if b(A);b(C).
Однако эта последняя версия теперь допускает решения, которые не всегда приемлемы. Например. plus(0, nonnumber, nonnumnber)
теперь выполняется успешно, тогда как вы можете захотеть, чтобы он завершился неудачей.
Конечно, вы также можете поэкспериментировать с сокращениями, но имейте в виду, что использование cut чрезвычайно подвержено ошибкам. По крайней мере, вы должны комбинировать это с соответствующими тестами, которые часто исключают «повышение эффективности».
Я понял, что подхожу к неправильному пути, мне нужен был не разрез (!
), а другой набор правил, которые находят X
и Y
путем "разрушения" Z
, пока он не достигнет 0, поскольку X
и Y
увеличиваются только при уменьшении Z
, им никогда не будут присвоены невозможные значения:
plus(0,0,0).
plus(s(A),B,s(C)) :- plus(A,B,C).
plus(0,s(B),s(C)) :- plus(0,B,C).
Похожие вопросы
Новые вопросы
prolog
Пролог - это наиболее часто используемый язык логического программирования. Он поддерживает недетерминированное программирование через хронологическое обратное и обратное согласие через объединение. Не используйте этот тег, как пролог и эпилог.