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