Recursion _integer(X)过程是如何工作的?

Recursion _integer(X)过程是如何工作的?,recursion,prolog,Recursion,Prolog,我在Mellish,Clocksin的书中读到了关于Prolog的内容,并得出以下结论: is_integer(0). is_integer(X) :- is_integer(Y), X is Y + 1. 通过查询?-is_integer(X)。零输出很容易,但如何得到1、2、3、4 我知道仅仅解释写作是不容易的,但我会感激任何尝试 在第一个结果X=0I点击然后查询变成is\u integer(0)或仍然是is\u integer(X) 我花了很长时间寻找对这个问题的好解释。提前感谢。这一

我在Mellish,Clocksin的书中读到了关于Prolog的内容,并得出以下结论:

is_integer(0).

is_integer(X) :- is_integer(Y), X is Y + 1.
通过查询
?-is_integer(X)。
零输出很容易,但如何得到1、2、3、4

我知道仅仅解释写作是不容易的,但我会感激任何尝试

在第一个结果
X=0
I点击
然后查询变成
is\u integer(0)
或仍然是
is\u integer(X)


我花了很长时间寻找对这个问题的好解释。提前感谢。

这一点直指是什么让Prolog如此有趣和困难。你绝对不傻,只是完全不同

这里有两条规则。备选方案的存在导致了选择点的产生。您可以将选择点视为Prolog看到另一种继续计算的方法的时刻。Prolog总是按照规则在数据库中出现的顺序来尝试规则,这将与规则在源文件中出现的顺序相对应。因此,当您发出查询
is_integer(X)
时,第一条规则匹配并将X与0统一。这是你的第一个结果

当你按“;”时您告诉Prolog失败,这个答案是不可接受的,这会触发回溯。Prolog要做的唯一一件事就是尝试输入另一条规则,该规则从
is_integer(Y)
开始。Y是一个新变量;它可能会,也可能不会被实例化为与X相同的值,到目前为止,您还没有看到任何不这样做的原因

这个调用,
is_integer(Y)
实质上重复了迄今为止尝试的计算。它将输入第一条规则,
是整数值(0)
,然后重试。此规则将成功,Y将与0统一,然后X将与Y+1统一,并且该规则将退出,并将X与1统一

当你按“;”时同样,Prolog将返回最近的选择点。这一次,最近的选择点是调用
is_integer(Y)
,在
is_integer/1
的第二条规则中。因此调用堆栈的深度更大,但我们没有保留第二条规则。实际上,在前一个位置激活第二条规则时,每个后续结果都将通过在该位置从第一条规则回溯到第二条规则而得到。我非常严肃地怀疑像前面这样的口头解释是否会有帮助,所以请看一下这个毫无意义的ASCII艺术,它描述了调用树是如何演变成这样的:

1     2       2
     /         \
    1           2
               /
              1

^     ^        ^
|     |        |
0     |        |
     1+0       |
             1+(1+0)
2            2
 \            \
  2            2
   \            \
    2            2
   /              \
  1                2
                  /
                 1

   ^             ^
   |             |
1+(1+(1+0))      |
 = 3          1+(1+(1+(1+0)))
                = 4
其中数字表示激活了哪个规则,级别表示调用堆栈的深度。接下来的几个步骤将演变如下:

1     2       2
     /         \
    1           2
               /
              1

^     ^        ^
|     |        |
0     |        |
     1+0       |
             1+(1+0)
2            2
 \            \
  2            2
   \            \
    2            2
   /              \
  1                2
                  /
                 1

   ^             ^
   |             |
1+(1+(1+0))      |
 = 3          1+(1+(1+(1+0)))
                = 4

请注意,我们总是通过将堆栈深度增加1并达到第一个规则来产生一个值。

Daniel的答案非常好,我只想提供另一种方法来看待它

以这个基于TNT的自然数的简单序言定义为例(因此0是
0
,1是
s(0)
,2是
s(0))
等):

陈述的意思很清楚。(1) 表示0是一个数字。(2) 如果
N
是一个数字,则说明
s(N)
是一个数字。使用自由变量调用时:

?- n(X).
它给出预期的
X=0
(来自(1)),然后查看(2),并进入
n/1
的“新”调用。在这个新调用中,(1)成功,对
n/1
的递归调用成功,(2)成功调用
X=s(0)
。然后看一下新调用的第(2)部分,依此类推

这是通过在第二条的开头统一来实现的。然而,没有什么能阻止你说:

n_(0).
n_(S) :- n_(N), S = s(N).
这只是延迟了
S
S(N)
的统一,直到对
N(N)
进行评估之后。由于求值
n_un(n)
和统一之间没有发生任何变化,因此当使用自由变量调用时,结果是相同的

你知道这是如何与你的
is_integer/1
谓词同构的吗

一句警告。正如评论中指出的,这个问题:

?- n_(0).
以及相应的

?- is_integer(0).
具有不终止的恼人属性(你可以用任何自然数呼叫它们,而不仅仅是0)。这是因为在递归地到达第一个子句并且调用成功之后,第二个子句仍然会被计算。此时,您已经“通过”了第一个子句的递归结束


上面定义的
n/1
不受此影响,因为Prolog可以通过查看两个子句头来识别,其中只有一个可以成功(换句话说,这两个子句是互斥的)。

Prolog使用“回溯”。在它给出解决方案
X=0
后,您点击
,它将移动到
is_integer(X)
,因为它已经完成
is_integer(0)
。当它作为子句调用
is\u integer(X)
时,对
is\u integer(Y)
的递归调用将返回到
is\u integer(0)
作为新调用。您可以在prolog解释器中打开
跟踪。
并观察发生的情况。:)是的,我确实看了它的踪迹,但它没有到达我。看看我是怎么看的:1)查询与事实匹配,X=0。当你点击第二个
,回溯会将您带到最后一个选择点,这将是对第一次实例化的
Y=0
is_integer(Y)
的最后一个调用。下次它将实例化
Y=1
,原因与您的第一次
相同实例化
X=1
。调用嵌套得越来越深,给您提供
2
3
,等等…Y如何得到=1?从我的观点来看,is_integer(Y)总是与is_integer(0)匹配,Y总是=0。抱歉这么愚蠢。请看下面的回答:
n\u1
现在永远不会终止,而当数量有限时,
n/1
会终止solutions@false您当然是正确的,但是演示的
是\u integer/1