Пункт Пролог заканчивается индивидуально, но не вместе
Так
?- canCall(mary, Person).
работает и заканчивается и
?- canFind(mary, Person).
также работает и заканчивается. Но почему-то
?- canCall(mary, Person), canFind(mary, Person).
не прекращается. Что может быть возможной причиной?
2 ответа
(То, что вы на самом деле имели в виду: запросы завершаются индивидуально, но иногда их соединение не прекращается)
Вы обнаружили здесь очень фундаментальный аспект свойств завершения Пролога. Давайте посмотрим на это с помощью следующей чистой программы 1:
canFind(mary, john).
canCall(mary, bob).
canCall(A, B) :-
canCall(B, A).
?- canCall(mary, Person).
Person = bob
; ...
?- canFind(mary, Person).
Person = john.
Все выглядит отлично! Давайте проверим этот код, чтобы каждый мог его использовать. Теперь ваш несчастный коллега пытается:
?- canCall(mary, Person), canFind(mary, Person).
* LOOPS *
О нет, это петли! Может быть, мне просто нужно изменить порядок целей:
?- canFind(mary, Person), canCall(mary, Person).
* LOOPS *
Снова!
Конечно, вы тоже расстроены. В конце концов, вы старательно тестировали этот код. И это прекратилось. Или сделал это?
Два понятия прекращения
Это одна из самых запутанных вещей в Прологе: у нас есть (как минимум) два разных понятия завершения запроса. Тот, который вы тестировали, (иногда) называется экзистенциальным завершением. Тем не менее, я скорее рекомендую назвать это просто находит ответ. Это чрезвычайно хрупко, как вы испытали.
И если запрос находит не только ответ, но и все из них и завершает запрос, то это называется универсальным завершением или просто прекращением. Если программисты Prolog говорят, что запрос завершается, это означает, что он завершается повсеместно.
Итак, как мы можем наблюдать универсальное завершение? Просто спросите все ответы. В GNU-Prolog вы вводите. В других системах вам придется забивать SPACE или ; Возвращайтесь, пока не закончите, или ваши усталые глаза или ваши запястные туннели не остановят это.
?- canCall(mary, Person).
Person = bob
; Person = bob
; Person = bob
; Person = bob
; Person = bob
; ...
Итак, здесь мы видим, что существует бесконечно много ответов (на самом деле, мы, конечные существа, должны были бы доказать это, но поверьте мне на данный момент).
Нет ли более дешевого способа наблюдать это? Без всей этой стены текстовых ответов? Вы можете "отключить" ответы, добавив условие, которое никогда не будет выполняться, false
,
Так что спросите:
? - canCall (Мэри, Персона), false.
Каким может быть результат такого запроса? Никогда не может быть true
, Это может быть только false
, если это прекратится. Таким образом, с помощью этого запроса мы просто проверяем свойство завершения программы.
Теперь соединение двух (универсально) завершающих запросов всегда будет завершено. Так что этот вид терминации гораздо более надежен.
Есть еще много интересных свойств универсального завершения. Например, вы можете поменять порядок предложений (то есть фактов и правил) так, как вам нравится: независимо от того, в каком порядке они находятся, все программы имеют одинаковое свойство завершения.
Другое - это то, что вы можете легко найти источник прерывания в ваших программах с помощью фрагмента сбоя. Начните читать с этим.
В командно-ориентированных языках программирования это понятие отсутствует. Однако с итераторами у вас есть довольно похожие понятия: если итератор создает первый элемент, это будет соответствовать экзистенциальному завершению, и если будет создано конечное число элементов, то есть, если после конечного числа next
это закончено, что будет соответствовать универсальному завершению. Вид.
1 На самом деле, в нечистых программах у вас есть все виды бессмысленного поведения. Так что бессмысленно их рассматривать.
Я полагаю, когда ты бегаешь индивидуально, Person
объединяет в два разных значения. Проверь это.