您在问为什么查询divisors2(40,R) 会出现无限循环。我几乎想用failure-slice 向你解释这一点。唉……
... 答案是:不,你没有无限循环!你的程序也会找到答案。这是
R = [1, 2, 4, 5, 8, 10, 20, 40]
这对我来说看起来很合理。它们按升序排列,您想要一个降序列表,但除此之外,这是一个完美的答案。不开玩笑。但是,我怀疑您没有足够的耐心得到答案。对于 36 我需要:
?- time(divisors2(36,R)).
% 10,744,901,605 inferences, 2248.800 CPU in 2252.918 seconds (100% CPU, 4778061 Lips)
R = [1, 2, 3, 4, 6, 9, 12, 18, 36]
非常不寻常...对于最多包含 36 个微不足道整数的列表,Prolog 需要 10 744 901 605 次推理,即少于 234。这会敲响警钟吗?无论如何,您的程序存在问题。事实上,有两个相当独立的问题。我们怎样才能找到它们?
也许我们看错了一面。只需返回查询。我们的第一个错误是我们如何使用 Prolog 的顶层。得到答案给我们留下了深刻的印象。但是 Prolog 为我们提供了进一步的答案!事实上:
?- time(divisors2(36,R)).
% 10,744,901,605 inferences, 2248.800 CPU in 2252.918 seconds (100% CPU, 4778061 Lips)
R = [1, 2, 3, 4, 6, 9, 12, 18, 36] ;
% 10 inferences, 0.000 CPU in 0.000 seconds (82% CPU, 455892 Lips)
R = [1, 2, 3, 4, 6, 9, 12, 18] ;
% 917,508 inferences, 0.192 CPU in 0.192 seconds (100% CPU, 4789425 Lips)
R = [1, 2, 3, 4, 6, 9, 12, 36] ...
这太乏味了。也许一个小例子就足够了?
?- divisors2(6,R).
R = [1, 2, 3, 6] ;
R = [1, 2, 3] ;
R = [1, 2, 6] ;
R = [1, 2] ;
R = [1, 3, 6] ;
R = [1, 3] ;
R = [1, 6] ;
R = [1] ;
R = [2, 3, 6] ;
R = [2, 3] ;
R = [2, 6] ;
R = [2] ;
R = [3, 6] ;
R = [3] ;
R = [6] ;
R = [] ;
false.
绰绰有余!也许我们坚持最小的例子[] 并重申它:
?- divisors2(6,[]).
true ;
false.
显然,这不是我们所期望的。我们希望这失败。如何定位问题? Prolog 中有一种通用的调试策略:
如果目标过于笼统,则将程序专门化。
我们可以通过添加更多目标来专门化程序,以便上述查询仍然成功。我将添加 false 和一些 (=)/2 目标。 false 特别有趣,因为它消除了整个子句:
?- 除数2(6,[])。
范围(I,I,[I]):-
I = 6。
范围(I,K,[I|L]) :-
K = 6,
我 K=6。
除数1([H|T],S,X):- false,
除数1(T,W,X),
Z 是 X mod H,
Z=0,
S=[H|W]。
除数1([_|T],S,X):-
S = [], X = 6,
除数1(T,S,X)。
除数2(X,结果):-
X = 6,结果 = []。
范围(1,X,结果1),
除数1(结果1,结果,X)。
在其余部分的某个地方,有些东西太笼统了!其实divisors1/3的递归规则太笼统了。您修改后的这个新程序称为 slice,它是我们原始程序的专业化。
解决这个问题的几种方法,最幼稚的方法是像这样添加相应的条件:
除数1([],[],_)。
除数1([H|T],S,X):-
除数1(T,W,X),
0 =:= X mod H,
S=[H|W]。
除数1([H|T],S,X):-
除数1(T,S,X),
0 =\= X 模 H。
但是,程序的性能并没有提高。为了看到这一点,我将再次专门研究这个程序:
divisors1([],[],_) :- false。
除数1([H|T],S,X):-
除数1(T,W,X),
假,
0 =:= X mod H,
S=[H|W]。
除数1([H|T],S,X):-
除数1(T,S,X),
假,
0 =\= X mod H。
因此:无论 false 后面是什么,该程序都会尝试至少 3 * 2^N 推断长度为 N 的列表。
通过将递归目标放在最后,我们可以避免这种情况。