我无法弄清楚这出错的地方。请注意,我对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).
答案 0 :(得分:6)
@Junuxx'答案是解决方案的一个步骤;你的程序还有另一个问题。但第一步:@Junuxx发现了问题并修复了它。尼斯。但是你怎么能发现这样的问题呢?实际上,你问过»无限循环 - 但是如何?«
Prolog的优点在于,您通常可以将循环程序本地化为程序的一小部分。这样的片段称为failure-slice。那就是:没有更多的眼睛疮阅读冗长的节目!
让我们回到您的计划。如果您加载它,您将收到如下消息:
Warning: /usager/SO/paranoid.pl:13: Singleton variables: [Result]
这让你已经暗示了一些可能错误的事情。唉,这是不你当前最关心的问题。你最大的问题是目标test
循环!
那么你怎么能用很少的努力来实现实际的循环呢?
单向将启动一个跟踪器,它将逐步显示Prolog如何执行此程序。但是,跟踪器会向您显示许多不相关的细节。详细信息,您在Prolog中编程时无需了解。细节,这填补了你的思想,很有可能你会完全错过实际的问题。因此,除非你想花时间在闪烁的线条上,否则远离示踪剂。
另一种方式是将目标false
添加到您的计划中。记住,你的程序已经循环,所以这样的额外目标不会对你造成太大伤害。为什么要使用您从未想过的false
目标来破坏您的程序?这是因为这些false
目标将通过隐藏“不相关”的部分来帮助您检测程序中非终止的罪魁祸首。这是如此,多亏了以下观察:
如果,故障片段(=您的破坏程序)未终止然后,原始程序也不会终止。
从某种意义上说,失败片是你的程序没有终止的原因。或者更强烈地说:只要你不改变故障片中的可见部分;也就是说,只要您通过修改在故障切片中看不到的部件来尝试运气,问题就会持续存在!保证!这不是最好的保证,但它比失明更好。
这是我作为失败片获得的。我删除了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 失败切片循环,然后整个程序在任何情况下,证明故障切片的非终止通常会更容易,因为片段更短))。
一些观察结果:最初,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.
看看Ys
:没有人关心它,它只是“移交”。只有第一个和第三个参数可以保证终止!
有关详情,请参阅标记failure-slice。
<小时/>
某些限制适用!在禁止的地方无效!您只能使用纯粹的单调Prolog程序进行上述推理。实际上,你的程序中的一些良性副作用也是可以的。只要它们不影响控制流程。
您的计划还有其他问题。运行printSentence([you]), false
查看它!回溯和副作用不容易聚集在一起。对于初学者来说,最好的方法是尽量避免副作用。有关示例,请参阅this question和that answer,了解如何在编程问题中消除无用的副作用。
为什么不直接致电transform([you, are, my, only hope], Xs)
或maplist(replace,[you, are, my only, hope], Xs)
?它让你再次专注于相关部分!
答案 1 :(得分:1)
这应该有效。请注意,transform([],Result)
中有一个单身人士。此外,追加不会以您尝试使用它的方式起作用,但您通常在正确的轨道上。
transform([], []).
transform([Word|Rest], [Replacement|RestOfResult]) :-
replace(Word, Replacement),
transform(Rest, RestOfResult).