2016-09-22 115 views
1

我最近在學習Prolog,並且很難讓它運行得如何。作爲一個例子,我已經設定了一個任務來創建一個Prolog精神病學家,它將輸入信息轉化爲一個問題,我「我認爲我不適」變成了「你爲什麼認爲你不舒服?」。Prolog寫入無限循環

但是,到目前爲止,我的代碼在退出時會生成一個無限循環。它在另一個未綁定的變量上調用printSentence的重做,並將其添加到輸出列表的末尾,並永遠這樣做。

這裏是我的代碼:

/* 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). 

輸入,

answer(['I', 'think', 'therefore', 'I', 'am'],Output). 

產生這些結果事情永遠

?- 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|...] 

道歉,如果這件事情小啞巴我已經還沒有完全掌握Prolog的內在陰謀。

在此先感謝。

回答

2

你寫了switchPronouns([], _).,這意味着對於一個空輸入,輸出可以是任何東西。當然這是不對的;你真正想要的是對於空輸入,輸出也是空的:switchPronouns([], []).

它進入一個無限循環的原因是因爲它最終會調用printSentence([H|T]),並帶有一個無效的變量T(當輸出可能是'任何東西',而Prolog還沒有它的值時,它會離開它非實例)。現在Prolog試圖匹配遞歸子句,並且看到如果T爲空列表,則打印問號。這是第一個找到的解決方案。但是,Prolog並不知道你是否真的認爲它是空的列表,所以它也會嘗試第二個子句:如果T是非空的[_H | _T]列表,那該怎麼辦?在這種情況下,頭部和尾部都是未知的。所以它打印未分配的頭部,並繼續與尾部的遞歸調用。但尾巴再次是一個未知的價值!所以我們以無限遞歸結束。


你會發現,在更新後switchPronouns,輸出仍然不正確。雖然現在有答案的數量有限,它給出一堆答案,你不要指望:

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] 
... 

其原因在於switchWord。例如,如果輸入的單詞是'I',Prolog能夠將其與條款switchWord('I', 'you').相匹配。但是,它也可以匹配switchWord(H, H).!你真正想要的是使用「真實」匹配,並且只有在沒有映射的情況下才保持該單詞不變。

我會寫這樣的事情:

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

現在你最終得到單一的預期答案!


我在這裏看到的另一個小問題:answer([], _)。就像上面這樣,這表示如果輸入是空的,輸出可以是任何東西。你可能想造成這個原因始終未能表明,沒有一個答案:

answer([], _) :- write('Why are you silent? Talk to me.'), fail. 

最後,通常的Prolog謂詞都寫在snake_case,沒有駝峯。這主要是一個偏好問題,但使用該標準可以讓其他程序員更容易閱讀代碼。

+0

這是一個非常完整的答案非常感謝!這個回答有助於我理解Prolog的工作原理 – SHolmes

1

你可以改變answer([], _)answer([], []),改變switchPronouns([], _).switchPronouns([], []). 的原因是_匹配任何東西:空列表,列表中的一個元素,two..and的推移,所以你強迫它是空的清單。 也有更多的一個問題:

?- 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]. 

它給由於switchWord/2一些錯誤的答案。你也可以改變:

answer(Input, Output) :- thinkMatch(Input, Output), printSentence(Output). 

answer(Input, Output) :- thinkMatch(Input, Output), printSentence(Output),!. 

這將減少這些錯誤的輸出:

?- answer(['I', 'think', 'therefore', 'I', 'am'],Output). 
Why do you think therefore you are ? 
Output = ['Why', do, you, think, therefore, you, are]. 
+0

哦,好吧,現在很有意義,非常感謝,昨天我的大腦有點慢了。我沒有跑過'!'它之前做了什麼? – SHolmes

+0

它被稱爲切割,當prolog試圖證明它嘗試所有可能的解決方案的謂詞時,如果已經找到了解決方案,則切斷(!)操作符切割一些可能的解決方案。那裏的地方!上面提到的是當它找到第一個正確的解決方案時阻止它。改變!的地方!你可以管理例如兩個給出兩個解決方案,並削減一切。所以它取決於哪裏!被放置... – coder