Prolog:删除谓词如何提供甚至列表的开头

时间:2018-05-03 18:18:38

标签: prolog

我当然误解了Prolog的工作方式。这个问题是具体的,虽然我正在寻找解释而不是编程问题的直接解决方案; a"怎么"问题而不是“给我代码”#34;问题

这是我要问的Prolog删除谓词的定义。

delete(A, [A|B], B).  
delete(A, [B, C|D], [B|E]) :-  
    delete(A, [C|D], E).  

我的误解是,在我看来,这是一个如

之类的电话
delete(a, [3,a,b,c,d], X).  

应该成功返回[3,b,c,d](它确实如此),但是会调用

delete(a,[1,2,3,a,b,c,d], X)  

应该无法返回解决方案。但是,后一个调用实际上确实返回了[1,2,3,b,c,d]的良好解决方案。

对我来说这样的原因如下。 (我认为我们现在越来越接近我无法理解的Prolog。)参考这部分定义,

delete(A, [B, C|D], [B|E]) . . . 

看起来只有头部前面的一个元素(即B)(即C)将被包含在响应中。这就是为什么我无法理解

的原因
delete (a, [1,2,3,a,b,c,d], X)

设法返回的答案不仅包括3,还包括12,作为解决方案列表的一部分,但确实如此(如[1,2,3,b,c,d] )。任何人都可以解释Prolog如何使用列表,在这个非常具体的场景中,以一种方式显示为什么它将12视为解决方案的一部分,而不仅仅是3?非常感谢。

2 个答案:

答案 0 :(得分:4)

让我们看看有问题的查询会发生什么:

?- delete(a,[1,2,3,a,b,c,d], X).

由于a1无法统一,因此第一条规则失败。但是,第二条规则与A=aB=1C=2D=[3,a,b,c,d]匹配。因此,递归目标被称为:

delete(a,[2|[3,a,b,c,d]],E)

同样,第一条规则不匹配,因为a2不同。第二条规则再次成功,这次是A=aB=2C=3D=[a,b,c,d]。因此,递归调用:

delete(a,[3|[a,b,c,d]],E)

然而,第一条规则失败,第二条规则以A=aB=3C=aD=[b,c,d]成功。因此,递归调用:

delete(a,[a|[b,c,d]],E)

现在,在第一条规则的标题中,a=a已成功统一,因此规则会以A=aB=[b,c,d]成功。回过头来看,这会产生:

E=[b,c,d], B=3 therefore [B|E]=[3|[b,c,d]]=[3,b,c,d] 

E = [3,b,c,d], B=2 therefore [B|E]=[2|[3,b,c,d]]=[2,3,b,c,d] 

E = [2,3,b,c,d], B=1 therefore [B|E]=[1|[2,3,b,c,d]]=[1,2,3,b,c,d] 

所以Prolog告诉你,确实有一个解决方案:

?- delete(a,[1,2,3,a,b,c,d], X).
X = [1, 2, 3, b, c, d]

现在,您可以按;

询问是否还有其他解决方案
?- delete(a,[1,2,3,a,b,c,d], X).
X = [1, 2, 3, b, c, d] ;

Prolog回到开放的选择点并遵循递归规则直到D=[]。由于[]=[A|B]无法统一,因此第一个规则的下一个递归调用失败。第二条规则也失败了,因为[]=[B,C|D]也无法统一。所以Prolog尽职尽责地报告说,没有更多的解决方案:

?- delete(a,[1,2,3,a,b,c,d], X).
X = [1, 2, 3, b, c, d] ;
false.

答案 1 :(得分:1)

作为一小部分背景,我们可以考虑一个单链表,由两个"构造函数":[],空列表和[H|T]组成,其中{ {1}}是列表头部的一些值,H,尾部,H之后剩余的列表。

这里有两个条款。第一个是:

T

我可能会以delete(A, [A|B], B). 的方式对此略有不同。这是启用统一的条款,例如:

delete(Head, [Head|Tail], Tail)

这是有效的,因为?- delete(3, [3,a,b,c,d], X). X = [a, b, c, d] 将统一[H|T]=[3,a,b,c,d]H=3。试试吧。

现在让我们花一点时间思考一下Prolog如何选择一个条款。每一步,Prolog都在尝试统一。因此,当提交T=[a,b,c,d]时,Prolog会尝试将delete(a, [1,2,3,a,b,c,d], X)delete(H, [H|T], T)统一起来。这不可能成功,因为a!= 1.所以Prolog再次使用第二个子句,我们发现自己在这里:

delete(a, [1,2,3,a,b,c,d], X)

现在举个例子,Prolog将尝试用delete(A, [B, C|D], [B|E]) :- delete(A, [C|D], E). 统一条款的头部。这是成功的,因为没有特别的原因delete(A=a, [B=1,C=2|D=[3,a,b,c,d]], [B=1|E])不能等于a,A不能等于1,B不能等于2,C不能等于D。所以现在Prolog进展到[3,a,b,c,d],它会尝试调用delete(A, [C|D], E)。如果您在REPL中启用delete(a, [2,3,a,b,c,d], E),您将看到这一点:

trace

您通过第二次调用看到Prolog现在有兴趣找到?- trace. true. [trace] ?- delete(a, [1,2,3,a,b,c,d], X). Call: (8) delete(a, [1, 2, 3, a, b, c, d], _3240) ? creep Call: (9) delete(a, [2, 3, a, b, c, d], _3498) ? creep ,这是一个新变量,而不是前一次调用的变量。我可以向你保证,在新的事情发生之前,同样的事情会发生几次。

delete(a, [2,3,a,b,c,d], _4120)

然而,在下一次调用中,可以激活第一个子句:

   Call: (9) delete(a, [2, 3, a, b, c, d], _3498) ? creep
   Call: (10) delete(a, [3, a, b, c, d], _3510) ? creep

这一次,变量与某些东西统一了。这是因为您的第一个子句是基本案例。现在回顾一下你的定义,你可以看到第二个子句中的第三个参数是 Call: (11) delete(a, [a, b, c, d], _3522) ? creep Exit: (11) delete(a, [a, b, c, d], [b, c, d]) ? creep ,这意味着当[B|E]B与某个东西统一时它将获取一个值。因此,跟踪的其余部分是该子句的退出,此变量已与某些内容统一。每一次,发生的事情只是在递归调用前置于结果之前从列表中删除的E

B

你有它。