Reputation: 198
I'm recently learning Prolog and am having trouble getting it to function how I want it to. As an example I've been set a task to create a Prolog Psychiatrist which takes input and turns it into a question, i,e "I think I am unwell" becomes "Why do you think you are unwell?".
However my code so far is generating an infinite loop when it exits. It calls a redo on printSentence with another unbound variable tacking it onto the end of the Output list and doing this forever.
Here is my code:
/* printSentence simply calls the in-built Prolog write function. */
printSentence([]) :- write('?').
printSentence([H|T]) :- write(H),write(' '), printSentence(T).
answer([], _) :- write('Why are you silent? Talk to me.').
answer(Input, Output) :- thinkMatch(Input, Output), printSentence(Output).
thinkMatch(['I', 'think'|Rest], ['Why', 'do', 'you', 'think'|SwitchedRest]) :- switchPronouns(Rest, SwitchedRest).
switchPronouns([], _).
switchPronouns([H|T], [R|SwitchedRest]) :- switchWord(H, R),switchPronouns(T, SwitchedRest).
switchWord('I', 'you').
switchWord('myself', 'yourself').
switchWord('am', 'are').
switchWord('you', 'me').
switchWord('yourself', 'myself').
switchWord(H, H).
The input,
answer(['I', 'think', 'therefore', 'I', 'am'],Output).
produces these results going on forever,
?- Input = ['I', 'think', 'therefore', 'I', 'am'],answer(Input, Output).
Why do you think therefore you are ?
Input = ['I', think, therefore, 'I', am],
Output = ['Why', do, you, think, therefore, you, are] ;
_G4395 ?
Input = ['I', think, therefore, 'I', am],
Output = ['Why', do, you, think, therefore, you, are, _G4395] ;
_G4398 ?
Input = ['I', think, therefore, 'I', am],
Output = ['Why', do, you, think, therefore, you, are, _G4395, _G4398] ;
_G4401 ?
Input = ['I', think, therefore, 'I', am],
Output = ['Why', do, you, think, therefore, you, are, _G4395, _G4398|...]
Apologies if it's something small and dumb I've yet to fully grasp the inner machinations of Prolog.
Thanks in advance.
Upvotes: 1
Views: 445
Reputation: 12992
You could change answer([], _)
to answer([], [])
and also change switchPronouns([], _).
to switchPronouns([], []).
The reason is that _
matches with anything: the empty list, the list with one element, with two..and goes on, so you force it to be empty list.
Also there is one more problem:
?- answer(['I', 'think', 'therefore', 'I', 'am'],Output).
Why do you think therefore you are ?
Output = ['Why', do, you, think, therefore, you, are] ;
Why do you think therefore you am ?
Output = ['Why', do, you, think, therefore, you, am] ;
Why do you think therefore I are ?
Output = ['Why', do, you, think, therefore, 'I', are] ;
Why do you think therefore I am ?
Output = ['Why', do, you, think, therefore, 'I', am].
It gives some wrong answers due to switchWord/2
. You could change also:
answer(Input, Output) :- thinkMatch(Input, Output), printSentence(Output).
to
answer(Input, Output) :- thinkMatch(Input, Output), printSentence(Output),!.
which will cut these wrong output:
?- answer(['I', 'think', 'therefore', 'I', 'am'],Output).
Why do you think therefore you are ?
Output = ['Why', do, you, think, therefore, you, are].
Upvotes: 1
Reputation: 2477
You wrote switchPronouns([], _).
, which means that for an empty input, the output can be anything. Of course this is not right; what you really want is that for an empty input, the output is also empty: switchPronouns([], []).
.
The reason it goes into an infinite loop is because it will end up calling printSentence([H|T])
with an uninstantiated variable for T
(when the output can be 'anything' and Prolog doesn't have a value for it yet, it leaves it uninstantiated). Now Prolog tries to match the recursive clause and sees that if T is en empty list, the question mark is printed. This is the first found solution.
However, Prolog doesn't know if you really meant for it to be the empty list, so it also tries the second clause: what if T is a non-empty list of the form [_H|_T]? In this case, both head and tail are unknown. So it prints the unassigned head, and continues with the recursive call with the tail. But the tail is an unknown value again! So we end up in an infinite recursion.
You will notice that after updating the switchPronouns
, the output is still not correct. Although there is now a finite number of answers, it gives a bunch of answers that you don't expect:
Why do you think therefore you are ?
Output = ['Why', do, you, think, therefore, you, are]
Why do you think therefore you am ?
Output = ['Why', do, you, think, therefore, you, am]
...
The reason for this lies in switchWord
. If for example the input word is 'I', Prolog is able to match it to the clause switchWord('I', 'you').
. However, it can also match it to switchWord(H, H).
! What you really want is to use the "real" match, and only keep the word unchanged if there is no mapping.
I'd write that something like this:
switchWord(I, O) :- word_map(I, R) -> O = R ; O = I.
word_map('I', 'you').
word_map('myself', 'yourself').
word_map('am', 'are').
word_map('you', 'me').
word_map('yourself', 'myself').
And now you end up with the single, expected answer!
Another small issue I see here: answer([], _)
. Just like above, this says that if the input is empty, the output can be anything. You may want to cause this cause to always fail to indicate that there isn't an answer:
answer([], _) :- write('Why are you silent? Talk to me.'), fail.
Finally, generally Prolog predicates are written in snake_case, not camelCase. This is mostly a matter of preference, but using the standard makes the code easier to read for other programmers.
Upvotes: 2