3

我不知道这是哪里出了问题。请注意,我对 Prolog 很陌生,我确信我错过了一些东西——只是不知道那可能是什么。有人可以帮我吗?

谢谢,这是我的代码:

printSentence([]).   
printSentence([W|[]]) :-
    write(W),
    write('.'),
    nl.  
printSentence([W|R]) :-
    write(W),
    write(' '),
    printSentence(R).

transform([], Result).  
transform([Word|Rest], Result) :-
    replace(Word, Replacement),
    append(Result, Replacement, NewResult),
    transform(Rest, NewResult).

replace(my, your).
replace(i, you).
replace(you, me).
replace(am, are).
replace(Word, Word).

test :-
    X = [you, are, my, only, hope],
    transform(X, Result),
    printSentence(Result).
4

2 回答 2

6

@Junuxx 的回答是解决问题的一步;您的程序中还有另一个问题。但第一步退后一步:@Junuxx 发现了问题并修复了它。好的。但是你怎么能发现这样的问题呢?实际上,您问的是“无限循环——但如何?”

Prolog 中很酷的地方在于,您通常可以将循环程序本地化为程序的一个非常小的片段。这样的片段称为。那就是:阅读冗长的节目不再令人眼花!

让我们回到你的程序。如果加载它,您将收到如下消息:

警告:/usager/SO/paranoid.pl:13:
    单例变量:[结果]

这已经为您提供了一些很可能是错误的提示。唉,这不是你目前最关心的问题。您最大的问题是目标test循环!

本地化不终止

那么,你如何能够不费吹灰之力地意识到什么是真正的循环呢?

一种方法是启动跟踪器,它将逐步向您展示 Prolog 如何执行该程序。但是,跟踪器会显示很多不相关的细节。细节,在 Prolog 中编程时不需要了解。细节,填满你的脑海,以至于你很可能会完全错过实际的问题。因此,除非您想花时间在充满闪烁线条的屏幕上,否则请远离示踪剂。

另一种方法是将目标添加false到您的程序中。请记住,您的程序已经循环,因此这些额外的目标不会对您造成太大伤害。false为什么要用这些你一开始就不想写的目标来破坏你的程序呢?这是因为这些false目标将通过隐藏“不相关”部分来帮助您检测程序中未终止的罪魁祸首。之所以如此,要归功于以下观察:

如果失败切片(=您被破坏的程序)没有终止,那么原始程序也不会终止。

从某种意义上说,故障片是程序不终止的一个原因。或者更强烈地说:只要您不更改 failure-slice 中的可见部分;也就是说,只要你只是通过修改故障切片中不可见的部分来碰碰运气,问题就会持续存在!保证!这不是最好的保证,但总比盲目好。

这是我得到的失败片段。我删除printSentence/1了,因为它不再在片段中使用。我添加了append/3. 一些 Prologs 提供append/3了一个你不能修改的内置谓词。在这种情况下,请使用其他名称,例如local_append/3- 只是不要忘记替换所有出现的地方!

附加([],Zs,Zs):-。
附加([X|Xs],Ys,[X|Zs]):-
   追加(Xs,Ys,Zs),变换([],结果):-
变换([字|休息],结果): -
    替换(字,替换),
    追加(结果,替换,新结果),变换(休息,新结果)替换(我的,你的):-的。
替换(我,你):-
替换(你,我)。
替换(我,是):-
替换(字,字): -

测试 :-
    X = [你,是,我的,唯一的,希望],
    变换(X,结果),printSentence(结果)

当我加载这个故障片时,我得到:

?- 测试。
错误:超出本地堆栈

这很好地表明程序不会终止。在我有限的硬件上,它反而耗尽了所有资源。((学究起来,这个程序可能仍然会终止,它可能只需要太多资源。但请记住:如果失败切片循环,则我们有这个,然后整个程序循环。无论如何,证明失败的非终止-切片通常会更容易,因为片段更短))。

一些观察:最初,transform/2曾经是递归的。现在,它不再是了。剩下的唯一递归是在append/3. 因此,我首先查看目标append(Result, Replacement, NewResult),然后尝试找出变量可能是什么。最简单的是第三个参数:NewResult是我们片段中唯一出现的,因此我们可以将其替换为_. 第二个参数的变量Replacement总是me. 第一个参数(这里我现在要看看test/0)将是一个未实例化的变量。所以我们要考虑目标append(_, me, _)

简单运行append(_, me, _), false看看这个目标不会终止!您也可以通过检查故障切片来看到这一点。再说一遍:

附加([],Zs,Zs):-。
附加([X|Xs],Ys,[X|Zs]):-
   追加(Xs,Ys,Zs),

Ys:没人管,就是“交”了。只有第一个和第三个参数可以保证终止!

有关更多信息,请参阅标签


印刷精美

某些限制适用!禁止的地方无效!您只能使用纯粹的单调 Prolog 程序进行上述推理。实际上,程序中的一些良性副作用也是可以的。只要它们不影响控制流。


另一个问题

您的程序还有另一个问题。跑printSentence([you]), false过去看看!回溯和副作用不会轻易聚集在一起。对于初学者来说,最好的办法是一起避免副作用。有关如何消除编程问题中无用的副作用的示例,请参见这个问题那个答案。为什么不打电话transform([you, are, my, only hope], Xs)maplist(replace,[you, are, my only, hope], Xs)直接打电话?它让您再次专注于相关部分!

于 2012-11-12T21:00:09.233 回答
1

这应该有效。请注意,您在transform([],Result). 此外, append 在您尝试使用它的方式中不起作用,但您通常走在正确的轨道上。

transform([], []).

transform([Word|Rest], [Replacement|RestOfResult]) :-
    replace(Word, Replacement),
    transform(Rest, RestOfResult).
于 2012-06-03T12:17:18.543 回答