如何阻止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).