如何解决如何使用 call_with_depth_limit/3
我试图在 SWI-Prolog 中使用 call_with_depth_limit/3
来实现迭代深化,但我不明白它是如何工作的,或者它行为不端。我有一个发生以下情况的示例:
?- call_with_depth_limit(mygoal,29,Result).
Result = 29 ;
Result = 25 ;
Result = 27 ;
Result = 27 ;
false.
?- call_with_depth_limit(mygoal,26,Result).
Result = depth_limit_exceeded ;
false.
根据文档,如果目标可以用极限最大递归或更少来证明,它应该会成功。在限制为 30 的第一次调用中,我们看到结果为 25,因此我希望以 26 的限制调用它会成功。 我在模块中使用了约束处理规则,以防那里可能有一些交互使其行为不端。
编辑:在玩弄伊莎贝尔的回答后,我想我明白它的行为了:
- 它像往常一样运行深度优先搜索,但如果达到 Limit+1 深度,它就好像失败了一样。
- 失败的分支计入结果。
- 每次在成功回答后回溯时,它都会将 Result 重置为堆栈的当前深度。
看这个例子:
loop :- loop.
succeed(0).
succeed(N) :- N > 0,N1 is N - 1,succeed(N1).
fail(N) :- N > 0,fail(N1).
?- call_with_depth_limit(succeed(0),1000,Result).
Result = 1 ;
false.
?- call_with_depth_limit(fail(50);succeed(0),Result).
Result = 53 ;
false.
% It tries loop until Limit+1 and therefore this is the Result
?- call_with_depth_limit(loop;succeed(0),Result).
Result = 1001 ;
false.
% In the second result it has to unroll the stack from 100 before trying the next,so Result is 100.
?- call_with_depth_limit(loop;succeed(100);succeed(0),Result).
Result = 1001 ;
Result = 103 ;
false.
?- call_with_depth_limit(loop;succeed(0);succeed(0),Result).
Result = 1001 ;
Result = 3 ;
false.
% If it gets to the end,and it has reached Limit+1 since the last successful result it returns depth_limit_exceeded.
?- call_with_depth_limit(loop;succeed(100);succeed(0);loop,Result).
Result = 1001 ;
Result = 103 ;
Result = depth_limit_exceeded.
解决方法
我不相信 SWI-Prolog 甚至为 call_with_depth_limit/3
实现了自己的规范。至少我读过:
如果 Goal 可以在没有比 Limit 级别更深的递归的情况下被证明,则 call_with_depth_limit/3 成功,将 Result 绑定到证明期间使用的最深递归级别。否则,如果在证明过程中超过限制,则 Result 与 depth_limit_exceeded 统一...
暗示Result
永远不会大于Limit
。但是有了这个程序:
succeed_with_depth(3) :-
succeed(3).
succeed_with_depth(1) :-
succeed(1).
succeed(0).
succeed(N) :-
N > 0,N1 is N - 1,succeed(N1).
我观察到(SWI-Prolog 7.6.4):
?- call_with_depth_limit(succeed_with_depth(N),5,Result).
N = 3,Result = 5 ;
N = 1,Result = 6 ;
false.
证明更浅的目标会导致更深的递归。超过限制但仍然成功的递归。
就我个人而言,阅读文档时,我希望在回溯时获得与直接调用 succeed_with_depth(1)
相同的深度:
?- call_with_depth_limit(succeed_with_depth(1),Result).
Result = 3 ;
false.
或者在 succeed_with_depth
的最外层添加 1 个深度用于回溯?那应该仍然给 Result = 4
而不是 6
。
编辑: 正如 rajashekar 在评论中指出的那样,在 succeed/1
的第一个子句中添加一个剪切会改变预期的意外行为。我认为这进一步表明 SWI-Prolog 的行为被破坏了:唯一的区别是切断了回溯将立即失败的选择。在任何后续计算中都没有实际更深的递归。
编辑 2:为了说明我为此设想的语义很简单,并且它实际上可以用于实现迭代深化,这里是一个小元- 足以从上面执行我的程序的解释器:
interpret_with_depth_limit(Goal,Limit,Result) :-
interpret_with_depth_limit(Goal,Result).
interpret_with_depth_limit(_Goal,Current,depth_limit_exceeded) :-
Current >= Limit,!.
interpret_with_depth_limit(Builtin,N,_Limit,N) :-
builtin(Builtin),!,call(Builtin).
interpret_with_depth_limit((A,B),Result) :-
!,interpret_with_depth_limit(A,ResultA),integer(ResultA),interpret_with_depth_limit(B,ResultB),integer(ResultB),Result is max(ResultA,ResultB).
interpret_with_depth_limit(Goal,Result) :-
N1 is N + 1,N1 < Limit,clause(Goal,Body),interpret_with_depth_limit(Body,N1,Result).
builtin(true).
builtin(_ > _).
builtin(_ is _).
这不会在回溯过程中保留深度信息,因此回溯的行为与调用目标的更具体实例时相同:
?- interpret_with_depth_limit(succeed_with_depth(N),6,Result = 3 ;
false.
?- interpret_with_depth_limit(succeed_with_depth(3),Result).
Result = 5 ;
false.
?- interpret_with_depth_limit(succeed_with_depth(1),Result).
Result = 3 ;
false.
迭代深化,以正确的顺序(即首先找到最浅的证明)找到每个答案,然后:
call_succeedingdepth(Goal,Depth) :-
between(1,infinite,Limit),Depth is Limit - 1,interpret_with_depth_limit(Goal,Depth).
测试:
?- call_succeedingdepth(succeed_with_depth(N),Depth).
N = 1,Depth = 3 ;
N = 3,Depth = 5 ;
% nontermination
我认为对于非终止无能为力;您通常会将其用于具有无限多个答案的目标。
,我试图通过使用这个程序来了解 call_with_depth_limit/3
的工作原理:
% a <- 1st call
% / \
% b g <- 2nd call
% /
% c <- 3rd call
% / \
% g d <- 4th call
% |
% e <- 5th call
arc(a,b).
arc(a,g).
arc(b,c).
arc(c,g).
arc(c,d).
arc(d,e).
path(X,X,[X]).
path(X,Z,[X|R]) :- arc(X,Y),path(Y,R).
获得的结果:
?- call_with_depth_limit(path(a,g,P),4,D).
P = [a,b,c,g],D = 4 ;
P = [a,D = 5 ;
false.
似乎答案是:
-
P = [a,D = 4
表示 4 次调用以获得第一个解。 -
P = [a,D = 5
表示 5 次调用以获得第二个解决方案(注意,在获得第二个解决方案之前,必须探索失败路径[a,d,e]
并且第 5 个调用导致失败 - 这个事实证明了答案D = 5
).
另一个查询:
?- call_with_depth_limit(path(a,3,D = 4 ;
false.
我们可以看到搜索在 4 次调用 (D = 4
) 后回溯,但是获得解 [a,g]
所需的第四次调用导致失败(因为 depth_limit
为 3)并且确实不产生结果。
[编辑] 另一个场景:在这个新场景中,在谓词 path/3
的第一个子句中添加了一个剪切,以避免扩展已经是解决方案的路径(否则,搜索将在同一路径上再向下尝试一步,并在找到第二个解决方案之前以深度 5 失败)。
% a <- 1st call
% / \
% b g <- 2nd call
% /
% c <- 3rd call
% /
% g <- 4th call
%
% <- 5th call
arc(a,g).
path(X,[X]) :- !.
path(X,R).
在这个新场景中,我们有:
?- call_with_depth_limit(path(a,D = 2.
我们观察到:
-
在第二个场景中,找到第二个解决方案所达到的最深递归级别是 2。
-
然而,在第一种情况下,找到第二个解决方案的最深递归级别是 5,因为在找到第二个解决方案之前,搜索必须尝试扩展路径
[a,g]
并探索路径 { {1}}(因此,解决方案证明过程中使用的最深递归级别[a,e]
为 5)。
请务必注意,如 SWI-Prolog 文档中所述,[a,g]
绑定到证明过程中使用的最深递归级别特定强> 解决方案,不是找到此解决方案的级别。
Result
如果 Goal 可以在没有比 Limit 级别更深的递归的情况下被证明,
call_with_depth_limit/3 成功,将 Result 绑定到使用的最深递归级别
在证明过程中。否则,Result 与 depth_limit_exceeded 统一,如果
证明过程中超出限制,或者如果目标失败则整个谓词失败
不超过限制。
迭代深化深度优先搜索
为call_with_depth_limit(:Goal,+Limit,-Result)
找到一个最浅的解,不超过Goal
:
Limit
以下是一些示例(两种情况的答案相同):
ids(Goal,Limit) :-
between(1,Depth),call_with_depth_limit(Goal,Depth,Result),Result \= depth_limit_exceeded,!.
,
难道不是第一个成功的结果需要深度为29,这显然过分了,然后就炸了?那么下一个需要深度为 25 的结果将永远不会被尝试。
,深度限制由内部机械保护。这可能与基于理论模型计算的深度不同。 [...] 因此, call_with_depth_limit/3 可能仍然在理论上应该在有限时间内完成的程序上无限循环。
所以它的“深度”不同于基于理论模型计算的深度。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。