查看下面的代码:
multiple(X,0).
multiple(X,Y) :- lt(0,X), lt(0,Y), diff(Y,X,D), multiple(X,D).
碰巧有问题。供您参考:
lt / 2是第一个参数是否小于第二个参数。
diff / 3是第三个参数是否等于第一个参数减去第二个参数。
lt / 2和diff / 3正确定义。
定义中是否存在逻辑错误?是假设0是每个数字的倍数是有问题的,还是其他地方的逻辑错误?我得到正确答案,但是查询进入无限循环,我认为。
编辑:
这是其他定义。
natNum(0).
natNum(s(X)) :- natNum(X).
lt(0,s(X)) :- natNum(X).
lt(s(X),s(Y)) :- lt(X,Y).
sum(0,X,X).
sum(s(X),Y,s(Z)) :- sum(X,Y,Z).
diff(X,Y,Z) :- sum(Z,Y,X).
?- multiple(X, s(s(s(s(s(s(0))))))).
其中s(0)
是1,s(s(0))
是2,依此类推。它给出了X的所有所需答案,但是在最后一个答案之后,它被卡住了。我假设处于无限递归循环中?
答案 0 :(得分:3)
您的程序中正在发生什么?它会永远循环吗?或者由于您最近几十年来没有更新硬件而只花了一些时间?我们不能说。 (实际上,我们可以通过查看您的程序来判断,但这暂时太复杂了。)
我们可以轻松完成的工作就是缩小这种昂贵工作的来源。而且,这对您的程序没有深入的了解。让我们从查询开始:
| ?- multiple(X, s(s(s(s(s(s(0))))))).
X = s(0) ? ;
X = s(s(0)) ? ;
X = s(s(s(0))) ? ;
X = s(s(s(s(s(s(0)))))) ? ;
** LOOPS or takes too long ***
没有更简单的方法来做到这一点吗?所有这些分号输入。相反,只需将 false
添加到查询中即可。通过这种方式,不再显示找到的解决方案,我们可以集中精力处理这种令人讨厌的循环。而且,如果需要的话,您还可以在程序中添加 false
个目标!通过这样的目标,推理的数量可能会减少(或保持不变)。如果生成的片段(称为failure-slice)正在循环,则这是原始程序循环的原因:
multiple(_X,0) :- false. multiple(X,Y) :- lt(0,X), false,lt(0,Y), diff(Y,X,D), multiple(X,D).natNum(0) :- false. natNum(s(X)) :- natNum(X), false. lt(0,s(X)) :- natNum(X), false.lt(s(X),s(Y)) :- false, lt(X,Y). ?- multiple(X, s(s(s(s(s(s(0))))))), false. ** LOOPS ***
您识别您的程序吗?仅剩下循环所需的那些部分。而且,实际上在这种情况下,我们有一个无限循环。
要解决此问题,我们需要在其余可见部分中进行一些修改。我会去寻找lt/2
的第一个子句可以概括为lt(0, s(_))
的地方。
但是等等!为什么可以概括我们有一个自然数的要求呢?查看您所写的事实multiple(X,0).
。您也没有要求X
是自然数。这种过度概括通常出现在Prolog程序中。它们以相对较低的价格改善了端接属性:有时它们过于笼统,但所有适合泛化的术语都不是自然数。它们是any
或[a,b,c]
之类的术语,因此,如果它们出现在某个位置,则说明它们不属于解决方案。
因此,我们的想法是将 false
个目标放入您的程序中,以使生成的程序(失败片段)仍然循环。在最坏的情况下,您将 false
放在错误的位置,程序将终止。通过反复试验,您可以获得最小的失败切片。现在所有经过的东西都是不相关!特别是diff/3
。因此(暂时)无需了解它。看剩下的程序就足够了。