什么';在prolog中建立自然数规则的正确方法是什么?

什么';在prolog中建立自然数规则的正确方法是什么?,prolog,Prolog,我想我在理解prolog时遇到了一些更大的问题,但因为我不能很好地表达它,所以我只关注一个问题 我想创建一个规则natural(X),如果X是1,2,3,4,… 更重要的是,我希望:natural(5)为true和natural(X)输出X=1;X=2 所以,我解释规则如下(伪逻辑): 或者,就序言而言: natural(1). natural(X) :- natural(X-1). 但是我遇到了一个问题-如果我尝试natural(5)我会得到无限递归 调试器说程序计算: natural(5

我想我在理解prolog时遇到了一些更大的问题,但因为我不能很好地表达它,所以我只关注一个问题

我想创建一个规则
natural(X)
,如果
X
是1,2,3,4,…
更重要的是,我希望:
natural(5)
为true
natural(X)
输出
X=1;X=2


所以,我解释规则如下(伪逻辑):

或者,就序言而言:

natural(1).
natural(X) :- natural(X-1).
但是我遇到了一个问题-如果我尝试
natural(5)
我会得到无限递归
调试器说程序计算:

natural(5)
 natural(5-1)
  natural(5-1-1)
   natural(5-1-1-1)
    natural(5-1-1-1-1)
     natural(5-1-1-1-1-1)
      natural(5-1-1-1-1-1-1)
...

我想问题出在
X-1
没有被评估?
让我们尝试解决这个问题:

natural(1).
natural(X) :-
  Y is X-1,
  natural(Y).
现在,
natural(5)
按预期工作
但是,如果我使用
natural(X)
我得到
X=1;例外:参数没有充分实例化(Y是X-1)


好吧,我想问题是我们试图评估那些可能没有价值的东西
如果我尝试使用
Y=X-1
我们返回到第一个问题
Y==X-1
返回
false

我发现唯一可行的解决方案是切换行和定义顺序:

natural(1).
natural(X) :-
  natural(Y),
  X is Y+1.
将最后一行更改为
=
将给出“+1+1…”结果<代码>=
只是失败了

此解决方案在生成
X=1时非常有效;X=2,但当我使用它作为检查(
natural(5)
)时,它的顺序是“0,(0,1),(0,1,2),(0,1,2,3),…”。是的,我得到了正确的结果,但这条路很长,不是我想象的那样。
如果我没有在以前的解决方案中看到更快的检查自然(5)的方法,我会停止在这里


所以,我错过了创建此规则的更好方法吗?


我想一种方法是将“真/假”查询与生成器查询分开。。。但是,有没有一种方法可以使其“如果可能的话进行评估”,即仅将常量与has变量分开
var(X-1)
在某种程度上是错误的…

通过使用
succ/2
处理自然现象通常会有很大的改进<正如您所发现的,Prolog需要代码>is/2
,以计算算术表达式,但它有一个单一的实例化模式:
-Number is+Expr
。如果没有约束条件,那么拥有一个完全开放的模式,例如
?Number is?Expr
,将是完全疯狂的

另一方面,
succ/2
有两种模式:
succ(+Pred,-succ)
succ(-Pred,+succ)
。也就是说:
suc(X,3)
unifies X=2和
suc(2,X)
unifies X=3。尽管成功(X,Y)仍然是一个错误。但是,您可以使用
succ/2
natural/1
构建解决方案,但需要注意:

natural(1).
natural(X) :- natural(X0), succ(X0, X).
从逻辑上讲,这应该与such(X0,X)、natural(X0)相同,但Prolog不是逻辑,它有一个求值顺序。这个技巧基本上会迫使你要求X是否是自然的,从X-1开始,立即转到X-2,然后向下,直到它达到1,然后它就可以备份并开始成功。如果您提供负数,它会立即失败,因为
succ/2
负数失败。这两种方式都适用,但存在一个严重的问题:

?- natural(X).
X = 1 ;
X = 2 ;
X = 3 ;
....

?- natural(5).
true ;
^CAction (h for help) ? abort
是的,如果我们在提供值后请求第二个结果,我们将得到一个无限循环。Prolog开始尝试找出5是否在5之后再次出现

避免该问题的一个简单解决方案是在/3之间使用

natural(X) :- between(1, inf, X).

?- natural(X).
X = 1 ;
X = 2 ;
X = 3 ;
...

?- natural(5).
true.
没有循环。这将是我首选的解决方案

除了
var/1
nonvar/1
之外,还有
ground/1
,它可以区分有变量的术语和没有变量的术语。你可以用它来区分(一边)5,3-1等和另一边的X,X-1。根据我的经验,像这样的情况通常会导致眼泪和向后的正确性问题,但在极端情况下,这可能是有道理的


此时,您可能会对Prolog的逻辑感到有点不高兴。标准体系的算法令人失望。但clpfd功能更强大、更灵活,许多人建议您首先了解这一点,因为它更擅长生成解决方案(
is/2
确实无法生成,但使用clpfd标记可以)。根据我的经验,
succ/2
与Peano算法非常接近,整数乱来通常是可以的,但是对于任何严重的事情,你都会想使用clpfd。

-
+
中的
意味着什么?
数字是+Expr
数字是?Expr
?喜欢你的散文。Thanks@NooneAtAll这些标记告诉您是否需要提供参数(也称为输入参数)——这是
+
——或者谓词是否将变量绑定为输出参数——这是
-
表示无论参数是否实例化,谓词都有效。
实例化模式对于这样的谓词很常见,谓词可以在其中生成或检查参数。@谢谢!我所知道的Prolog的85%都归功于你!“通过使用……
库(
首先使用定义自然数,处理自然数的能力通常会大大提高。在它里面,一切都是自然的,与
(is)/2
相比,它是非常困难和不完整的。然后,使用
库(clpfd)
,这更自然。想想:
X#>=0
natural(X) :- between(1, inf, X).

?- natural(X).
X = 1 ;
X = 2 ;
X = 3 ;
...

?- natural(5).
true.