转换句子会产生无限循环 - 但是如何?

时间:2012-06-03 12:07:47

标签: prolog text-manipulation failure-slice non-termination

我无法弄清楚这出错的地方。请注意,我对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).

2 个答案:

答案 0 :(得分:6)

@Junuxx'答案是解决方案的一个步骤;你的程序还有另一个问题。但第一步:@Junuxx发现了问题并修复了它。尼斯。但是怎么能发现这样的问题呢?实际上,你问过»无限循环 - 但是如何?«

Prolog的优点在于,您通常可以将循环程序本地化为程序的一小部分。这样的片段称为。那就是:没有更多的眼睛疮阅读冗长的节目!

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

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:没有人关心它,它只是“移交”。只有第一个和第三个参数可以保证终止!

有关详情,请参阅标记

<小时/>

精细打印

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


另一个问题

您的计划还有其他问题。运行printSentence([you]), false查看它!回溯和副作用不容易聚集在一起。对于初学者来说,最好的方法是尽量避免副作用。有关示例,请参阅this questionthat 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).