我是 prolog 的新手,我写的每一段代码都会变成一个无限循环。
我特别想看看 X 是否在 0 到 K - 1 的范围内。
range(X,X).
range(X,K) :- K0 is K - 1, range(X,K0).
我在代码背后的想法是我递减 K 直到 K0 等于 X,然后基本情况将开始。虽然我得到了一个无限循环,所以我的想法是错误的。
我是 prolog 的新手,我写的每一段代码都会变成一个无限循环。
我特别想看看 X 是否在 0 到 K - 1 的范围内。
range(X,X).
range(X,K) :- K0 is K - 1, range(X,K0).
我在代码背后的想法是我递减 K 直到 K0 等于 X,然后基本情况将开始。虽然我得到了一个无限循环,所以我的想法是错误的。
欢迎来到 Prolog 的奇妙世界!在学习 Prolog 时,您似乎试图跨越几个步骤,并且(并不奇怪)失败了。
理想情况下,你拿一本像《序言艺术》这样的书,从家庭关系开始。然后使用successor-arithmetics向自然数扩展,然后才转到(is)/2
。今天,(也就是说,大约从 1996 年开始)甚至有比使用SICStus 或 SWI 中(is)/2
的方法更好的方法。library(clpfd)
所以让我们看看你的程序会如何,使用successor-arithmetics。也许less_than_or_equal/2
会是一个更好的名字:
less_than_or_equal(N,N).
less_than_or_equal(N,s(M)) :-
less_than_or_equal(N,M).
?- less_than_or_equal(N,s(s(0))).
N = s(s(0)) ;
N = s(0) ;
N = 0.
它开箱即用!没有任何循环。那么出了什么问题呢?
后继算术依赖于自然数。但是您使用的整数也包含这些负数。对于负数,数字不再是有序的(有根据的,或诺特式的),你直接体验到了这种后果。所以坚持自然数!它们都是纯天然的,不含任何人为的负面成分。谁说“上帝创造了整数,其他一切都是人的工作”。一定是错的。
但现在回到你的程序。为什么它不终止?毕竟,你找到了答案,所以它不是完全错误的。不是吗?您试图将您在面向命令的语言中学到的控制流概念重新应用到 Prolog。好吧,Prolog 有两个相对独立的控制流,还有更多令人惊讶的东西,比如在运行时出现的实变量 (TM),在 Java 或 C# 中没有直接对应物。所以这个映射不起作用。当你称这个事实为“基本情况”时,我有点怀疑。您可能的意思是它是“终止条件”。但事实并非如此。
那么我们如何才能轻松地理解 Prolog 中的终止呢?最好的方法是使用failure-slice。false
我们的想法是,我们将通过在您的程序中插入目标来尝试使您的程序尽可能小。在任何地方。某些生成的程序仍然不会终止。这些是最有趣的,因为它们是不终止原始程序的原因!它们立即与您的问题有因果关系。而且它们更好,因为它们更短。这意味着更少的阅读时间。这里有一些尝试,我将删除不再相关的部分。
范围(X,X)。 范围(X,K):- K0 是 K - 1, false ,range(X,K0)。
不,上面没有循环,所以它不能告诉我们任何事情。让我们再试一次:
范围(X,X):-假。 范围(X,K):- K0 是 K - 1, 范围(X,K0),假。
这个range(X,1)
已经循环了。事实上,它是最小故障片。有了一点经验,您将学会毫不费力地看到那些。
我们必须更改可见部分中的某些内容以使其终止。例如,您可以添加K > 0
或执行@Shevliaskovic 建议的操作。
我相信最简单的方法是:
range(X,X).
range(X,K) :- X>0, X<K-1.
这是我的结果:
6 ?- range(4,4).
true .
7 ?- range(5,8).
true.
8 ?- range(5,4).
false.
正如已经指出的那样,如果您只想验证X
位于指定域内的简单方法是只检查条件:
range(X,K) :- X >= 0 , X < K .
否则,如果您希望自己range/2
具有生成性,则可以使用内置的between/3
:
range(X,K) :- integer(K) , K1 is K-1 , between(0,K1,X).
如果您的序言没有between/3
,这是一个非常简单的实现:
%
% the classic `between/3` wants the inclusive lower and upper bounds
% to be bound. So we'll make the test once and use a helper predicate.
%
between(Lo,Hi,N) :-
integer(Lo),
integer(Hi),
_between(Lo,Hi,N)
.
_between(Lo,Hi,Lo) :- % unify the lower bound with the result
Lo =< Hi % - if we haven't yet exceeded the inclusive upper bound.
. %
_between(Lo,Hi,N) :- % otherwise...
Lo < Hi , % - if the lower bound is less than the inclusive upper bound
L1 is Lo+1 , % - increment the lower bound
_between(L1,Hi,N) % - and recurse down.
. %