Prolog - Rules are correct, but not outputting the way it's supposed to?

喜欢而已 提交于 2019-12-23 20:18:18

问题


Clue

Four guests (Colonel Mustard, Professor Plum, Miss Scarlett, Ms. Green) attend a dinner party at the home of Mr. Boddy. Suddenly, the lights go out! When they come back, Mr Boddy lies dead in the middle of the table. Everyone is a suspect. Upon further examination, the following facts come to light:

  • Mr Boddy was having an affair with Ms. Green.
  • Professor Plum is married to Ms. Green.
  • Mr. Boddy was very rich.
  • Colonel Mustard is very greedy.
  • Miss Scarlett was also having an affair with Mr. Boddy.

There are two possible motives for the murder:

  • Hatred: Someone hates someone else if that other person is having an affair with his/her spouse.
  • Greed: Someone is willing to commit murder if they are greedy and not rich, and the victim is rich.

Part A: Write the above facts and rules in your Prolog program. Use the following names for the people: colMustard, profPlum, missScarlet, msGreen, mrBoddy. Be careful about how you encode (or don’t encode) symmetric relationships like marriage - you don’t want infinite loops! married(X,Y) :- married(Y,X) % INFINITE LOOP

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

Part B: Write a predicate, suspect/2, that determines who the suspects may be, i.e. who had a motive.

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

Part C: Add a single factto your database that will result in there being a unique suspect. Clearly indicate this line in your source comments so that it can be removed/added for grading.

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

Whenever I type in

suspect(Killer,mrBoddy).

I get

suspect(Killer,mrBoddy).
Killer = profPlum

I'm missing

Killer = colMustard.

Here's my source.

%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).

回答1:


The problem is the recursive rules of the predicates affair/2 and married/2. Attempting to use them easily leads to an endless loop (i.e. until the stack memory is exhausted). You must use a different predicate in each case to represent that if X is having an affair with Y, then Y is having an affair with X. You also need to change your definition of the suspect/2 predicate to call those new predicates.

To better understand why you get an endless loop, use the trace facilities of your Prolog system. Try:

?- trace, suspect(Killer, mrBoddy).

and go step by step.




回答2:


There are two kinds of problems with your program. One is on the procedural level: you observed that Prolog loops; the other is on the logical level — Prolog people call this rather the declarative level. Since the first annoying thing is this endless loop, let's first narrow that down. Actually we get:

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

You have now several options to narrow down this problem. Either, go with the other answer and call up a tracer. While the tracer might show you the actual culprit it might very well intersperse it with many irrelevant steps. So many that your mind will overflow.

The other option is to manually modify your program by adding goals false into your program. I will add as many false goals as I can while still getting a loop. The big advantage is that this way you will see in your source the actual culprit (or to be more precise one of potentially many such culprits).1 After trying a bit, this is what I got as 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).

All remaining parts of your program were irrelevant, that is, they are no longer used. So essentially the rule

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

is the culprit.

Now, for the declarative part of it. What does this rule mean anyway? To understand it, I will interpret :- as an implication. So provided what is written on the right-hand side is true, we conclude what is written on the left-hand side. In this case:

Provided X is married to Y and Y is married to X
we can conclude that
X is married to Y.

This conclusion concluded what we have assumed to be true anyway. So it does not define anything new, logically. You can just remove the rule to get same results — declaratively. So married(profPlum, msGreen) holds but married(msGreen, profPlum) does not. In other words, your rules are not correct, as you claim.

To resolve this problem, remove the rule, rename all facts to husband_wife/2 and add the definition

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

So the actual deeper problem here was a logical error. In addition to that Prolog's proof mechanism is very simplistic, turning this into a loop. But that is not much more than a welcome excuse to the original logical problem.2


Footnotes:
1 This method only works for pure, monotonic fragments. Non-monotonic constructs like not/1 or (\+)/1 must not appear in the fragment.
2 This example is of interest to @larsmans.



来源:https://stackoverflow.com/questions/20060840/prolog-rules-are-correct-but-not-outputting-the-way-its-supposed-to

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!