我开始学习Prolog,并首先了解了继承符号。
这就是我在Prolog中发现写Peano公理的地方。
请参阅PDF的第12页:
sum(0, M, M).
sum(s(N), M, s(K)) :-
sum(N,M,K).
prod(0,M,0).
prod(s(N), M, P) :-
prod(N,M,K),
sum(K,M,P).
我将乘法规则放入Prolog中。然后我做查询:
?- prod(X,Y,s(s(s(s(s(s(0))))))).
这意味着基本上找到6的因子。
以下是结果。
X = s(0),
Y = s(s(s(s(s(s(0)))))) ? ;
X = s(s(0)),
Y = s(s(s(0))) ? ;
X = s(s(s(0))),
Y = s(s(0)) ? ;
infinite loop
这个结果有两个问题:
所以...我的问题是:
我在无限循环中阅读了另一个答案。但我很感激有人根据这个场景做出回答。这对我很有帮助。
答案 0 :(得分:31)
如果您想深入研究终止属性,使用successor-arithmetics的程序是理想的研究对象:您知道先验他们应该描述的内容,因此您可以专注于更具技术性的细节。您需要了解几个概念。
解释它的最简单方法是考虑Goal, false
。如果Goal
普遍终止,则终止。那就是:查看跟踪器是最无效的方式 - 它们只向您显示一条执行路径。但你需要立刻了解所有这些!当你想要普遍终止时,也永远不会看答案,它们只会分散你的注意力。你已经看到了上面的内容:你有三个简洁而正确的答案,只有你的程序循环。因此,使用false
更好地“关闭”答案。这消除了所有分心。
您需要的下一个概念是failure slice。采用纯粹的单调逻辑程序并投入一些目标false
。如果生成的故障片没有终止(普遍),原始程序也不会终止。在您的例子中,请考虑:
prod(0,M,0) :- false. prod(s(N), M, P) :- prod(N,M,K), false,sum(K,M,P).
这些false
目标有助于删除程序中不相关的装饰品:其余部分清楚地向您展示了prod(X,Y,s(s(s(s(s(s(0))))))).
未终止的原因。它不会终止,因为该片段根本不关心P
!你希望第三个参数有助于使prod/3
终止,但是片段显示它都是徒劳的,因为P
没有出现在任何目标中。不需要繁琐的示踪剂。
通常,找到最小的故障切片并不容易。但是一旦找到了它,就可以确定它的终止或非终止属性。一段时间后,您可以使用直觉来想象一个切片,然后您可以使用您的理由来检查该切片是否相关。
失败片段的概念是如此引人注目:如果你想改进程序,你必须修改上面片段中可见部分的程序!只要你不改变它,问题就会持续存在。因此,失败切片是您计划中非常重要的一部分。
这是您需要的最后一件事:像cTI这样的终止推理器(或分析器)将帮助您快速识别终止条件。查看prod/3
和改进的prod2/3
here的推断终止条件!
编辑:由于这是一个家庭作业问题,我还没有发布最终解决方案。但为了说清楚,这是迄今为止获得的终止条件:
prod(A,B,C)terminates_if b(A),b(B). prod2(A,B,C)terminates_if b(A),b(B);b(A),b(C).
所以新的prod2/3
严格要比原来的程序好!
现在,您可以找到最终的程序。终止条件是:
prod3(A,B,C)terminates_if b(A),b(B);b(C).
首先,尝试找到prod2(A,B,s(s(s(s(s(s(0)))))))
的失败切片!我们希望它终止,但它仍然没有。因此,请使用该程序并手动添加false
个目标!剩下的部分会告诉你钥匙!
作为最后的提示:你需要添加一个额外的目标和一个事实。
编辑:根据请求,这是prod2(A,B,s(s(s(s(s(s(0)))))))
的失败切片:
prod2(0,_,0) :- false. prod2(s(N), M, P) :- sum(M, K, P), prod2(N,M,K), false. sum(0, M, M).sum(s(N), M, s(K)) :- false,sum(N,M,K).
请注意sum/3
的明确简化定义。它只说:0加任何东西都是什么。不再。因此,即使更专业的prod2(A,0,s(s(s(s(s(s(0)))))))
将循环,而prod2(0,X,Y)
优雅地终止......
答案 1 :(得分:17)
第一个问题(为什么)相当容易发现,特别是如果了解left recursion。当C 绑定时,sum(A,B,C)
绑定 A和B,但原始程序prod(A,B,C)不使用该绑定,而是递归还有A,B未绑定。
如果我们交换sum,prod我们从sum获得2个有用的绑定用于递归调用:
sum(M, K, P)
现在绑定了M,并将用于终止左递归。我们可以交换N和M,因为我们知道产品是可交换的。
sum(0, M, M).
sum(s(N), M, s(K)) :-
sum(N, M, K).
prod3(0, _, 0).
prod3(s(N), M, P) :-
sum(M, K, P),
prod3(M, N, K).
注意,如果我们交换M,K(即sum(K,M,P)),当用P未知调用prod3时,我们再次有一个非终止循环,但总之。
?- prod3(X,Y,s(s(s(s(s(s(0))))))).
X = s(s(s(s(s(s(0)))))),
Y = s(0) ;
X = s(s(s(0))),
Y = s(s(0)) ;
X = s(s(0)),
Y = s(s(s(0))) ;
X = s(0),
Y = s(s(s(s(s(s(0)))))) ;
false.
OT 我对cTI报告感到困惑:prod3(A,B,C)终止_if b(A),b(B); b(A),b(C)。