如何阻止prolog无限地检查不可能的解决方案?
假设以下程序:如何阻止prolog无限地检查不可能的解决方案?,prolog,backtracking,logic-programming,failure-slice,successor-arithmetics,Prolog,Backtracking,Logic Programming,Failure Slice,Successor Arithmetics,假设以下程序: nat(0). nat(s(N)) :- nat(N). /* 0+b=b */ plus(0,B,B) :- nat(B). /* (a+1)+b = c iff a+(b+1)=c */ plus(s(A),B,C) :- plus(A,s(B),C). 添加两个数字非常有效,但当我尝试以下类型的查询时: plus(Z,Z,s(0)). 在显然没有解决方案(即Z>s(0))很久之后,它继续搜索Z的可能值 我熟悉cut(!)操作符,直觉告诉我解决方案与此有关,我只是不确定
nat(0).
nat(s(N)) :- nat(N).
/* 0+b=b */
plus(0,B,B) :- nat(B).
/* (a+1)+b = c iff a+(b+1)=c */
plus(s(A),B,C) :- plus(A,s(B),C).
添加两个数字非常有效,但当我尝试以下类型的查询时:
plus(Z,Z,s(0)).
在显然没有解决方案(即Z>s(0)
)很久之后,它继续搜索Z
的可能值
我熟悉cut(
!
)操作符,直觉告诉我解决方案与此有关,我只是不确定在这种情况下如何使用它。/对于此类问题,0始终不是一个好的解决方案。对于更一般的查询,它通常会导致丢失有效的解决方案
取而代之的是考虑使用有限域约束,它在这种情况下工作得很好:
?- use_module(library(clpfd)).
true.
?- Z + Z #= 0.
Z = 0.
?- Z + Z #= 0, Z #> 0.
false.
编辑:根据要求,不使用CLP(FD)的可能解决方案:
plus(0, Y, Y).
plus(s(X), Y, s(Z)) :- plus(X, Y, Z).
我意识到我走错了方向,我需要的不是一个切割(!
),而是一组不同的规则,通过“分解”Z
直到它达到0,找到X
和Y
,因为Z
只有在Z>递减时才会递增,他们永远不会被赋予不可能的价值:
plus(0,0,0).
plus(s(A),B,s(C)) :- plus(A,B,C).
plus(0,s(B),s(C)) :- plus(0,B,C).
因此,您在这里要了解的是特定程序的精确终止属性。Prolog与其他编程语言有很大的不同,因为它有一个相对复杂的执行机制。特别是回溯与“正常分辨率”交织在一起,然后与统一相结合
最简单的方法来理解你的原始程序的问题是考虑下面的(参见其他例子的链接):
最好的办法似乎是重新制定你的计划。给你:
在Prolog中,我们通常喜欢进一步推广程序,也接受一些意外的解决方案,同时将其终止条件改进为:
然而,这最后一个版本现在承认了并非总是可以接受的解决方案。例如,plus(0,非数字,非数字)
现在成功了,而您可能希望它失败
当然,您也可以对cut进行一些实验,但请注意,使用cut极易出错。至少你应该将它与通常会抑制“效率增益”的适当测试结合起来。我可能应该明确提到这一点,但是,我正在寻找一种不使用任何库和使用后续算术的解决方案。@false感谢标签,我不知道这个概念有一个正式名称。@mat你是对的,把它作为回答,我会接受的。
plus(0,B,B) :- false, nat(B).
plus(s(A),B,C) :- plus(A,s(B),C), false.
plus(A,B,C)terminates_if b(A),b(B);b(A),b(C).
plus(A,B,C)terminates_if b(A),b(B);b(C).
plus(A,B,C)terminates_if b(A);b(C).