【问题标题】:Prolog write infinite loopProlog写死循环
【发布时间】:2016-09-22 05:38:48
【问题描述】:

我最近正在学习 Prolog,但无法让它按照我想要的方式运行。举个例子,我的任务是创建一个 Prolog Psychiatrist,它接受输入并将其转化为一个问题,即“我认为我不舒服”变成了“你为什么认为你不舒服?”。

但是,到目前为止,我的代码在退出时会生成一个无限循环。它在 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 的内部机制,这是一件小而愚蠢的事情。

提前致谢。

【问题讨论】:

    标签: prolog output infinite-loop


    【解决方案1】:

    你写了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 写的,而不是camelCase。这主要是一个偏好问题,但使用该标准可以让其他程序员更容易阅读代码。

    【讨论】:

    • 这是一个非常彻底的答案,非常感谢!这真的有助于我理解 Prolog 的工作原理
    【解决方案2】:

    您可以将answer([], _) 更改为answer([], []),也可以将switchPronouns([], _). 更改为switchPronouns([], []). 原因是 _ 匹配任何东西:空列表、一个元素的列表、两个元素的列表……然后继续,所以你强制它为空列表。 还有一个问题:

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

    【讨论】:

    • 哦,好吧,现在说得通了,非常感谢,昨天我的大脑有点慢。我没有遇到'!在它做什么之前的符号?
    • 它被称为cut,当prolog试图证明一个谓词时,它会尝试所有可能的解决方案,如果已经找到解决方案,cut (!) 运算符会剪切一些可能的解决方案。那个地方!当它找到第一个正确的解决方案时,放在上面就是这样阻止它。换个地方!例如,您可以管理两个给出两个解决方案并削减其他所有内容。所以要看在哪里!被放置...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-02
    • 2018-05-30
    • 1970-01-01
    相关资源
    最近更新 更多