让我们将问题分为三个部分:关于变量实例化的问题和我在该示例的变体中使用的累加器模式:
add(0,Y,Y).
add(s(X),Y,Z):-add(X,s(Y),Z).
以及关于您使用替换组合的示例的评论。
为了查看哪个规则(即 Horn 子句)匹配(其头部统一),Prolog 应用的是统一算法,它特别告诉我,如果我有一个变量,比如说 X 和一个函子,即 f(Y )这两个术语统一(关于发生检查的一小部分...检查但没关系atm)因此有一个替代项可以让您将一个转换为另一个。
当您的第二条规则被调用时,R 确实统一为 s(Z)。不要被 Prolog 赋予新的、未实例化的变量的内部表示吓到,它只是一个变量名(因为它以 '_' 开头),它代表一个代码(Prolog 必须有一种方法来表示不断新生成的变量和所以 _G648、_G649、_G650 等等)。当您调用 Prolog 过程时,您传递的未实例化的参数(在这种情况下为 R)用于包含过程完成执行时的结果,并且它将包含结果,因为在过程中的某个时刻调用它将被实例化(总是通过统一)。如果在某些时候你有一个 var,即 K 被实例化为 s(H)(或者 s(_G567),如果你愿意的话),
累加器模式取自函数式编程,简而言之,它是一种拥有变量的方法,即累加器(在我的例子中是 Y 本身),它有责任在某些过程调用之间进行部分计算。该模式依赖于递归,大致具有以下形式:
- 递归的基本步骤(我的第一条规则,即)总是说,由于您已经到达计算的结尾,您可以将部分结果(现在是总计)从累加器变量复制到输出变量(这是其中的步骤,通过统一你的输出变量被实例化!)
- 递归步骤告诉如何创建部分结果以及如何将其存储在累加器变量中(在我的情况下,我“增加”Y)。请注意,在递归步骤中,输出变量永远不会改变。
最后,关于您的示例,它遵循另一种模式,即替换的组合,我认为通过统一考虑累加器和实例化,您可以更好地理解。
- 它的基本步骤与累加器模式相同,但 Y 在递归步骤中永远不会改变,而 Z 会改变
- 它用于通过在到达基本步骤并且每个过程调用结束后在每个递归调用结束时部分实例化所有计算来将 Z 中的变量与 Y 统一。因此,在第一次调用结束时,Z 中的内部自由 var 已被 Y 中的值多次统一替换。请注意下面的代码,在您到达底部调用后,过程调用堆栈开始弹出并且您的部分vars (S1, S2, S3 for semplicity) 得到统一,直到 R 被完全实例化
这是堆栈跟踪:
add(s(s(s(0))),s(0),S1). ^ S1=s(S2)=s(s(s(s(0))))
add( s(s(0)) ,s(0),S2). | S2=s(S3)=s(s(s(0)))
add( s(0) ,s(0),S3). | S3=s(S4)=s(s(0))
add( 0 ,s(0),S4). | S4=s(0)
add( 0 ,s(0),s(0)). ______|