我试图理解为什么代码1工作正常但代码2输出错误:
remove(X,[],[]).
remove(X,[X|Y],Z):-remove(X,Y,Z). % [X|Y] is the input list
remove(X,[F|Y],[F|Z]):-remove(X,Y,Z). % code 1
remove(X,[],[]).
remove(X,[X|Y],Z):-remove(X,Y,Z). % [X|Y] is the input list
remove(X,[F|Y],Z):-remove(X,Y,[F|Z]). % code 2
考虑查询,
?- remove(x, [x,a,b,c,x,d], R).
代码2的问题在于,在某些时候,在提取第二个列表中的所有元素之后,它变为空。此时,在递归时,唯一可能的选择是第一个子句,因为第2个和第3个子句要求列表具有头部,而不是因为它是空的。
因此,它试图与第一个进行统一。现在,前两个谓词很好,但最后一个是有问题的。
也就是说,它试图统一[] = [d, c, b, a | _free_ ]
。这是不可能的,因为[]不是'自由变量'
然而,我的术语可能是错的......但我相信其实质是存在的。
你提出的两个谓词 - code 1
和code 2
--都被打破了。
你没注意到的原因是:查询。
#1)考虑this previous "answer"中显示的查询:
?- remove(x, [x,a,b,c,x,d], R). R = [a,b,c,d] % okay
好的?是的,但可能会有更多的答案。我们按吧↵!
; R = [a,b,c,x,d] % BAD! ; R = [x,a,b,c,d] % BAD! ; R = [x,a,b,c,x,d] % BAD! ; false. % (no more answers)
这三个答案都是无效的,因为每个R
包含一些x
。
底部像:不要只看一些查询答案。查看所有查询答案!
#2)Prolog程序包含不同类型的条款:事实,规则和查询。
查询是程序中非常重要的部分。
查询既是文档也是规范。最重要的是,它们使您能够将程序开发的“机械”部分委派给Prolog处理器。
那么哪些查询要写?
?- remove(x,[x,a,x,p],[a,p]). % x is first element
?- remove(x,[a,x,p,x],[a,p]). % x is last element
?- remove(x,[a,x,p],[a,p]).
?- \+ (remove(x,[a,x,p],Ls), dif(Ls,[a,p])).
?- remove(x,[],[]).
?- remove(X,[a,b,a,c,d,c,a,b],Xs).
底线:没有查询,你不是在编写代码,而是只编写文本。
#3)现在让我们来修复code 1
:看看两个递归条款!
remove(X,[X|Y],Z) :- remove(X,Y,Z). remove(X,[F|Y],[F|Z]) :- remove(X,Y,Z).
第一条规则将X
与第一个名单[X|Y]
联系起来。好!
第二条规则不是X
与[F|Y]
或[F|Z]
。坏!
通过在第二个子句中添加prolog-dif目标dif/2
,我们可以构建该连接。
#4)完成!这是谓词remove/3
的完整代码:
remove(_X, [], []). remove(X, [X|Y], Z) :- remove(X, Y, Z). remove(X, [F|Y], [F|Z]) :- dif(X, F), remove(X, Y, Z).