我正在使用SWI Prolog学习Prolog进行大学考试,我对以下问题的两种不同解决方案之间的差异有一些疑问:
定义子句 count(X,L,NumX)其中X是原子,L是 list和NumX是X出现在L中的次数。
这是第一个解决方案:
count0(_,[],0).
count0(A, [A|Tail], N) :-
count0(A,Tail,N1), % L'elemento cercato appare N1 volte nella sottolista
N is N1+1. % N vale N1+1
count0(A, [B|Tail], N) :-
A\=B, % A è diverso da B
count0(A,Tail,N). % N è il numero di occorrenze di A nella sottolista
这是第二个解决方案:
count1(_,[],0).
count1(A, [A|Tail], N) :- !,
count1(A, Tail, N1),
N is N1+1.
count1(A, [_|Tail], N) :- count1(A, Tail, N).
我的问题是我不理解它在第二个版本中扮演的角色
我知道CUT可以防止在放置CUT的特定点回溯。
该程序的第一个版本检查A是否与第二个规则中的B不同(我需要这个?如果第一个规则失败,那么这意味着A不与列表的HEAD统一,所以列表的头部与A)中的元素不同
第二个版本不会在第二个规则中执行此检查,但会在第一个规则中删除...
这可能取决于(在第二个版本中)如果我不阻止回溯发生的事实:在Prolog之后给我第一个(正确的)响应,如果我强制使用回溯;碰巧使用第二条规则:
count1(A, [_|Tail], N) :- count1(A, Tail, N).
在计算和分支中采用不同的分支我没有N是N + 1?
答案 0 :(得分:1)
第一个版本在第二个子句中留下一个选择点,而第二个版本在进入第二个子句时将该子句提交(带有剪切)。
第一个版本需要明确检查项目是否与列表头部不同,因为在回溯时,无论第二个子句是否成功,第三个子句都将被执行。
如果使用简单的1个元素的输入列表跟踪两个过程,您可以自己查看。
?- count0(a,[a], Count).
程序的第一个版本将项目与列表头部匹配,并执行递归。然而,如果需要,它将在那里留下选择点以查看其他替代方案。 然后递归因基本情况(空列表)而结束,并且得到 Count = 1 的结果。
如果您现在要求prolog获取其他替代方案,它仍然具有该选择点,因此它将尝试thirc子句。如果您没有明确地检查A和B是否不同,它将递归调用自身(再次使用空列表)并返回 Count = 0 ,这是一个错误的答案!
现在,您的程序的第二个版本(使用剪切的版本)。当prolog使用项目a进入第二个子句时,它会提交剪切,因此它不会留下选择点。现在你进行递归并以 Count = 1 的正确结果结束。
如果您现在要求prolog提供其他替代方案,它会说没有什么可以检查的。 由于削减,没有必要再检查A和B是否不同,因为您确定它们将是不同的,否则第二个子句将提交,第三个子句将不会被测试。