Prolog 序言:delete谓词如何提供列表的开头

Prolog 序言:delete谓词如何提供列表的开头,prolog,Prolog,我对Prolog的工作原理肯定有些误解。这个问题是具体的,尽管我正在寻找一个解释,而不是一个编程问题的直接解决方案;一个“如何”的问题,而不是一个“给我代码”的问题 下面是我要问的Prolog delete谓词的定义 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). delete(a,[1,2,3,a

我对Prolog的工作原理肯定有些误解。这个问题是具体的,尽管我正在寻找一个解释,而不是一个编程问题的直接解决方案;一个“如何”的问题,而不是一个“给我代码”的问题

下面是我要问的Prolog delete谓词的定义

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).  
delete(a,[1,2,3,a,b,c,d], X)  
应该成功返回
[3,b,c,d]
(它确实返回了),但是

delete(a, [3,a,b,c,d], X).  
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
?非常感谢。

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

?- 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)
delete(a,[a|[b,c,d]],E)
同样,第一条规则失败,第二条规则成功,分别是
A=A
B=3
C=A
D=[B,C,D]
。因此,递归调用:

delete(a,[3|[a,b,c,d]],E)
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.

作为一个小的背景,我们可以考虑一个单独的链表,由两个“构造函数”组成:<代码> []/COD>,空列表,和<代码> [Hyt] < /代码>,其中<代码> h <代码>是在列表的头部出现的某种值和<代码> t>代码>,尾部,是H.

后剩下的列表。 这里有两个条款。第一个是:

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
T=[a,b,c,d]
统一起来。试试看

现在让我们花一点时间思考Prolog如何选择一个从句。在每一步中,Prolog本质上都在尝试统一。因此,当出现
delete(a[1,2,3,a,b,c,d],X)
时,Prolog试图将
delete(H[H | T],T)
delete(a[1,2,3,a,b,c,d],X)
统一起来。这不可能成功,因为a!=1.因此,Prolog对第二条进行了另一次尝试,我们发现:

delete(A, [B, C|D], [B|E]) :-  
    delete(A, [C|D], E).  
现在,在这个例子中,Prolog将尝试用
delete(A=A[B=1,C=2 | D=[3,A,B,C,D]],[B=1 | E])统一子句的开头。这是成功的,因为没有特别的原因说明
A
不能等于A,
B
不能等于1,
C
不能等于2,
D
不能等于
[3,A,B,C,D]
。所以现在Prolog前进到
delete(A[C | D],E)
,它将尝试调用
delete(A[2,3,A,b,C,D],E)
。如果在REPL中启用
跟踪
,您将在工作中看到:

?- 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
通过第二次调用,您可以看到Prolog现在感兴趣的是查找
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

   Exit: (10) delete(a, [3, a, b, c, d], [3, b, c, d]) ? creep
   Exit: (9) delete(a, [2, 3, a, b, c, d], [2, 3, b, c, d]) ? creep
   Exit: (8) delete(a, [1, 2, 3, a, b, c, d], [1, 2, 3, b, c, d]) ? creep
X = [1, 2, 3, b, c, d] .

这就是结果。

“在递归调用在结果前面加上前缀之前从列表中剥离的
B
。”这是我没有得到的部分。可以说,Prolog会自动尝试它在下行过程中传递的内容。谢谢(两个答案都很好;这句话非常贴切。)@davo不客气。当然,更确切地说,Prolog建立了THO之间的关系