我当然误解了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
,还包括1
和2
,作为解决方案列表的一部分,但确实如此(如[1,2,3,b,c,d]
)。任何人都可以解释Prolog如何使用列表,在这个非常具体的场景中,以一种方式显示为什么它将1
和2
视为解决方案的一部分,而不仅仅是3
?非常感谢。
答案 0 :(得分:4)
让我们看看有问题的查询会发生什么:
?- delete(a,[1,2,3,a,b,c,d], X).
由于a
和1
无法统一,因此第一条规则失败。但是,第二条规则与A=a
,B=1
,C=2
和D=[3,a,b,c,d]
匹配。因此,递归目标被称为:
delete(a,[2|[3,a,b,c,d]],E)
同样,第一条规则不匹配,因为a
与2
不同。第二条规则再次成功,这次是A=a
,B=2
,C=3
和D=[a,b,c,d]
。因此,递归调用:
delete(a,[3|[a,b,c,d]],E)
然而,第一条规则失败,第二条规则以A=a
,B=3
,C=a
和D=[b,c,d]
成功。因此,递归调用:
delete(a,[a|[b,c,d]],E)
现在,在第一条规则的标题中,a=a
已成功统一,因此规则会以A=a
,B=[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
你有它。