@Junuxx'的回答是解决问题的第一步;您的程序中还存在另一个问题。但首先退后一步:@Junuxx 发现了问题并修复了它。好的。但怎样才能you发现这样的问题吗?其实你问的是»无限循环——但是如何?«
Prolog 的一个很酷的地方是,您通常可以将循环程序定位到程序的一个非常小的片段。这样的片段称为故障切片 /questions/tagged/failure-slice。那就是:阅读冗长的程序不再让人眼花缭乱!
让我们回到你的程序。如果加载它,您将收到如下消息:
Warning: /usager/SO/paranoid.pl:13:
Singleton variables: [Result]
这已经给了你一些很可能是错误的提示。唉,这是not您目前最关心的问题。你最大的问题是目标test
loops!
本地化非终止
那么如何才能轻松地了解实际循环的内容呢?
One way就是启动一个跟踪器,它将逐步向您展示 Prolog 如何执行该程序。但是,跟踪器会向您显示许多不相关的细节。细节,在 Prolog 中编程时不需要理解。细节,充满了你的大脑,这样你很可能会完全错过实际的问题。因此,除非您想花时间在充满闪烁线条的屏幕上,否则请远离示踪剂。
另一种方法就是添加目标false
到你的程序中。请记住,您的程序已经循环,因此这些额外的目标不会对您造成太大伤害。为什么要用这些破坏你的程序false
您一开始就不想写的目标?正是因为这些false
目标将通过隐藏“不相关”部分来帮助您检测程序中不终止的罪魁祸首。之所以如此,得益于以下观察:
If失败切片(=您被破坏的程序)不会终止then原始程序也不会终止。
从某种意义上说,失败切片是程序无法终止的一个原因。或者更强烈地说:只要你不改变 failure-slice 中的可见部分;也就是说,只要您只是通过修改故障切片中不可见的部分来碰碰运气,问题就会持续存在!有保证!这不是最好的保证,但总比盲目要好。
这是我得到的失败切片。我删除了printSentence/1
因为它不再在片段中使用。我添加了定义append/3
。一些 Prolog 提供append/3
作为您无法修改的内置谓词。在这种情况下,请使用另一个名称,例如local_append/3
– 只是不要忘记替换所有出现的地方!
append([], Zs, Zs) :- false.
append([X|Xs], Ys, [X|Zs]) :-
append(Xs, Ys, Zs), false.
transform([], Result) :- false.
transform([Word|Rest], Result) :-
replace(Word, Replacement),
append(Result, Replacement, NewResult), false,
transform(Rest, NewResult).
replace(my, your) :- false.
replace(i, you) :- false.
replace(you, me).
replace(am, are) :- false.
replace(Word, Word) :- false.
test :-
X = [you, are, my, only, hope],
transform(X, Result), false,
printSentence(Result).
当我加载这个失败切片时,我得到:
?- test.
ERROR: Out of local stack
这很好地表明该程序不会终止。在我有限的硬件上,它会耗尽所有资源。 ((说得迂腐一点,这个程序可能仍然会终止,它可能只需要太多的资源。但是请记住:我们有这个if故障切片循环,then整个程序循环。无论如何,证明故障切片的非终止通常会更容易,因为片段更短))。
一些观察:最初,transform/2
曾经是递归的。现在,它不再是了。剩下的唯一递归是在append/3
。所以我首先看目标append(Result, Replacement, NewResult)
我试图找出变量可能是什么。最简单的是第三个参数:NewResult
是我们片段中唯一出现的,因此我们可以将其替换为_
。第二个参数的变量Replacement
一直会me
。第一个论点(我现在要看看test/0
) 将是一个未实例化的变量。所以我们必须考虑目标append(_, me, _)
.
只需运行append(_, me, _), false
看到这个目标不会终止!您也可以通过检查失败切片来看到这一点。再说一遍:
append([], Zs, Zs) :- false.
append([X|Xs], Ys, [X|Zs]) :-
append(Xs, Ys, Zs), false.
Look at Ys
:没人关心,只是“交给”而已。只有第一个和第三个参数可能保证终止!
更多内容请见标签故障切片 /questions/tagged/failure-slice.
印刷精美
Certain restrictions apply! Void where prohibited! You can do above reasoning only with a pure, monotonic Prolog program. Actually, some benign side-effects as the ones you have in your program are OK too. As long as they do not affect the control-flow.
另一个问题
您的程序还有另一个问题。跑步printSentence([you]), false
看见了!回溯和副作用不会轻易聚集在一起。对于初学者来说,最好的是避免副作用。看这个问题 https://stackoverflow.com/q/9744641/772868 and 那个答案 https://stackoverflow.com/a/9791028/772868例如,如何消除编程问题中无用的副作用。
为什么不打电话transform([you, are, my, only hope], Xs)
or maplist(replace,[you, are, my only, hope], Xs)
直接地?它可以让您再次专注于相关部分!