Prolog - Правила правильные, но вывод не такой, как должен?

Подсказка

Четверо гостей (полковник Мастард, профессор Плам, мисс Скарлетт, мисс Грин) посещают званый обед в доме мистера Бодди. Внезапно гаснет свет! Когда они возвращаются, мистер Бодди лежит мертвый посреди стола. Каждый является подозреваемым. При дальнейшем рассмотрении выясняются следующие факты:

  • У мистера Бодди был роман с мисс Грин.
  • Профессор Плам женат на мисс Грин.
  • Мистер Бодди был очень богат.
  • Полковник Мастард очень жаден.
  • У мисс Скарлетт также был роман с мистером Бодди.

Возможны два мотива убийства:

  • Ненависть: кто-то ненавидит кого-то другого, если у этого другого человека роман с его/ее супругом.
  • Жадность: Кто-то готов совершить убийство, если он жаден и не богат, а жертва богата.

Часть A: Запишите приведенные выше факты и правила в своей программе на Прологе. Используйте следующие имена для людей: colMustard, profPlum, missScarlet, msGreen, mrBoddy. Будьте осторожны с тем, как вы кодируете (или не кодируете) симметричные отношения, такие как брак — вам не нужны бесконечные циклы! married(X,Y) :- married(Y,X) % INFINITE LOOP

?-suspect(Killer,mrBoddy)
Killer = suspect_name_1
Killer = suspect_name_2
etc.

Часть Б: Напишите предикат, подозреваемый/2, который определяет, кто может быть подозреваемым, т. е. у кого был мотив.

?-suspect(Killer,mrBoddy)
Killer = unique_suspect.

Часть C: Добавьте один факт в свою базу данных, который приведет к появлению уникального подозреваемого. Четко укажите эту строку в комментариях к исходному тексту, чтобы ее можно было удалить/добавить для оценивания.

?-suspect(Killer,mrBoddy)
Killer = unique_suspect.

Всякий раз, когда я печатаю

suspect(Killer,mrBoddy).

я получил

suspect(Killer,mrBoddy).
Killer = profPlum

я скучаю

Killer = colMustard.

Вот мой источник.

%8) Clue

%facts

affair(mrBoddy,msGreen).
affair(missScarlett, mrBoddy).
affair(X,Y) :- affair(X,Y), affair(Y,X).

married(profPlum, msGreen).
married(X,Y) :- married(X,Y), married(Y,X).

rich(mrBoddy).
greedy(colMustard).

%rules

hate(X,Y) :- married(X,Spouse), affair(Y,Spouse).
greed(X,Y) :- greedy(X), not(rich(X)), rich(Y).


%suspect

suspect(X,Y):- hate(X,Y).
suspect(X,Y):- greed(X,Y).

person BadJo0Jo0    schedule 19.11.2013    source источник
comment
Вы спрашивали Пролога о другом убийце? Используя ; после того, как вы напечатаете первое решение?   -  person ssBarBee    schedule 19.11.2013
comment
Вау, я не знал об этом. Большое спасибо!   -  person BadJo0Jo0    schedule 19.11.2013
comment
Вы попадете в бесконечный цикл при попытке получить второе решение.   -  person Paulo Moura    schedule 19.11.2013


Ответы (2)


Проблема в рекурсивных правилах предикатов affair/2 and married/2. Попытка их использования легко приводит к бесконечному циклу (т. е. до тех пор, пока память стека не будет исчерпана). Вы должны использовать разные предикаты в каждом случае, чтобы представить, что если у X роман с Y, то Y имеет роман с X. Вам также нужно изменить определение предиката suspect/2, чтобы вызвать эти новые предикаты.

Чтобы лучше понять, почему вы получаете бесконечный цикл, используйте средства трассировки вашей системы Prolog. Пытаться:

?- trace, suspect(Killer, mrBoddy).

и идти шаг за шагом.

person Paulo Moura    schedule 19.11.2013
comment
Спасибо! Я поймал бесконечный цикл, когда наконец получил его, чтобы попытаться вывести второго подозреваемого. У меня сейчас другая проблема. Как мне перейти к разделу C этой проблемы? Я не понимаю, как я могу добавить один факт, чтобы опровергнуть два других текущих факта и вернуть только одного подозреваемого. - person BadJo0Jo0; 19.11.2013
comment
Недостаточно фактов, чтобы исключить любого из подозреваемых. - person Paulo Moura; 19.11.2013
comment
Если я добавлю что-то вроде willKill :- greedy, как мне изменить подозреваемого, чтобы выбрать willkill в качестве окончательного ответа вместо возвращающихся людей только с мотивами? - person BadJo0Jo0; 19.11.2013
comment
На самом деле я имел в виду часть C. Я просто отредактировал ее обратно в исходный вопрос. Поэтому я добавил правило/факт willKill :- жадный. (Не уверен, что это правильный подход к этой проблеме) Я попытался изменить подозреваемого на подозреваемого (X, Y): - willKill (X) -> X; (ненависть(X,Y); жадность(X,Y)). Я получаю ошибку исключения. Также я понял, что если я закомментирую willKill, это полностью сломает мою программу. - person BadJo0Jo0; 19.11.2013
comment
Вы можете добавить предикат person/1, чтобы перечислить всех людей в головоломке, а затем переписать новый willKill/1 как willKill(X) :- person(X), .... Это позволит избежать исключения, связанного с тем, что переменная X не создается при вызове willKill(X). Но у вас по-прежнему будет два решения, поскольку одно решение невозможно вывести из ограниченного набора фактов. - person Paulo Moura; 19.11.2013

В вашей программе есть два типа проблем. Один из них находится на процедурном уровне: вы заметили циклы Пролога; другой находится на логическом уровне. Прологисты называют его скорее декларативным уровнем. Поскольку первая раздражающая вещь — это бесконечный цикл, давайте сначала сузим его. Собственно получаем:

?- suspect(Killer,mrBoddy).
Killer = profPlum ;
ERROR: Out of local stack

Теперь у вас есть несколько вариантов решения этой проблемы. Либо выберите другой ответ и вызовите трассировщик. Хотя трассировщик может показать вам фактического виновника, он вполне может перемежать его множеством не относящихся к делу шагов. Так много, что ваш разум переполнится.

Другой вариант – изменить программу вручную, добавив в нее цели false. Я добавлю столько целей false, сколько смогу, пока не зациклусь. Большим преимуществом является то, что таким образом вы увидите в своем источнике фактического виновника (или, если быть более точным, одного из потенциально многих таких виновников).1 Немного постаравшись, вот что я получил как failure-slice:

?- suspect(Killer,mrBoddy), false.

married(profPlum, msGreen) :- false.
married(X,Y) :- married(X,Y), false, married(Y,X).

hate(X,Y) :- married(X,Spouse), false, affair(Y,Spouse).

suspect(X,Y):- hate(X,Y), false.
suspect(X,Y):- false, greed(X,Y).

Все остальные части вашей программы были неактуальны, то есть больше не использовались. Так что по сути правило

married(X,Y) :- married(X,Y), married(Y,X).

является виновником.

Теперь о декларативной части. Что вообще означает это правило? Чтобы понять это, я буду интерпретировать :- как импликацию. Итак, если верно то, что написано в правой части, мы заключаем, что написано в левой части. В этом случае:

При условии, что X женат на Y, а Y женат на X
мы можем сделать вывод,
X женат на Y.

Этот вывод заключает в себе то, что мы и так считали истинным. Так что это не определяет ничего нового, логически. Вы можете просто удалить правило, чтобы декларативно получить те же результаты. Таким образом, married(profPlum, msGreen) выполняется, а married(msGreen, profPlum) нет. Другими словами, ваши правила неверны, как вы утверждаете.

Чтобы решить эту проблему, удалите правило, переименуйте все факты в husband_wife/2 и добавьте определение

married(M,F) :- husband_wife(M,F).
married(F,M) :- husband_wife(M,F).

Так что на самом деле более глубокая проблема здесь была логической ошибкой. В дополнение к этому механизм доказательства Пролога очень упрощен, превращая его в цикл. Но это не более чем приятное оправдание исходной логической проблемы2.


Сноски:
1 Этот метод работает только для чистых монотонных фрагментов. Немонотонные конструкции, такие как not/1 или (\+)/1, не должны появляться во фрагменте.
2 Этот пример представляет интерес для @larsmans.

person false    schedule 11.06.2014
comment
Я не согласен с вашим утверждением, что это логическая ошибка. m(x,y) ∧ m(y,x) → m(x,y) является излишним, но лишние аксиомы не меняют теории, которую выражает программа (это может быть в линейной логике, но Пролог основан на стандартной логике первого порядка). - person Fred Foo; 11.06.2014
comment
@larsmans: ОП утверждал, что правила верны, но для ОП они были неверны. Это все, что я утверждаю. И часто (правда, не всегда) такие ошибки идут рука об руку с процедурными ошибками. - person false; 11.06.2014
comment
Рассматриваемое правило является верным логически, как и всякая тавтология. Процедурно это неправильно, потому что вывод в глубину зацикливается. Алгоритм вывода в ширину (необязательный в SICStus и не слишком сложный для реализации в других прологах) останется незатронутым. - person Fred Foo; 11.06.2014
comment
@larsmans: При вашем конкретном использовании правильного не существует неправильных программ (за исключением некоторых необоснованных шагов, таких как успех для X=s(X)). Одна только ширина не решает эту проблему вообще. И: вы уверены, что имеете в виду SICStus? - person false; 11.06.2014
comment
@larsmans: Вы не имели в виду: снизу вверх, а не в ширину? - person false; 11.06.2014
comment
Я думал, что в SICStus есть опция «сначала в ширину», но сейчас я не могу найти ее в руководстве (и я не использовал ее уже много лет). В любом случае, IIRC позволяет построить мета-интерпретатор, ориентированный на ширину (или итеративное углубление), который может доказать любую истинную теорему программы на чистом Прологе, хотя он может войти в бесконечный цикл из-за ложности. - person Fred Foo; 11.06.2014
comment
@larsman: Это возможно, но оно не может обработать рассматриваемую программу. - person false; 11.06.2014
comment
Я полагаю, из-за отрицания, но он может обрабатывать тавтологическое правило для married, если запрос верен. Re: мое определение правильного, нет, есть неправильные программы даже без бесконечных сроков. Предположим, что модель программы такая, в которой [[foo]] = {x}, затем foo(y). — неправильная программа. - person Fred Foo; 11.06.2014
comment
@larsman: bfs может выдавать только все ответы (уже для монотонных программ), но не может остановиться! В любом случае married/2 было определено неверно, поскольку married(msGreen, profPlum). не соответствует действительности. - person false; 12.06.2014