列表操作的奇怪结果

时间:2014-10-25 08:44:17

标签: prolog prolog-dif

我试图在Prolog中为列表操作实现一些谓词。 一切都按预期工作。例如

append([],Ys,Ys).
append([X|Xs],Ys,[X|Zs]) :- append(Xs,Ys,Zs). 

示例查询:

?- append([1,2,3],[4,5,6],X).
X = [1,2,3,4,5,6].                   % OK

但我在'删除' -predicate方面遇到了麻烦。以下是它的实施方式:

delete(X,[Y|Ys],Zs) :- X == Y, delete(X,Ys,Zs).
delete(X,[_X|Ys],[_Z|Zs]) :- delete(X,Ys,Zs).
delete(_X,[],[]).

结果不佳的示例查询:

?- delete(4,[1,2,3,4],X).
X = [_G8975, _G8978, _G8981].       % BAD

我已经使用进一步的输入对其进行了测试,它总是返回预期长度的列表,以便它的工作方式。但为什么我只得到那些神秘的_GXXXX而不是数字?

非常感谢提前!

3 个答案:

答案 0 :(得分:4)

您的计划中有几个问题。但首先,作为初学者:

坚持Prolog的纯粹单调子集

在您的程序中,您正在使用不再位于该纯子集中的(==)/2。在您的情况下,请将其替换为(=)/2

始终查看所有答案

Prolog的顶级循环只显示第一个答案。您必须按; SPACE 来要求更多。您的程序(=)/2给出(更可读的变量):

?- delete(4,[1,2,3,4],X).
X = [_A,_B,_C] ;
X = [_A,_B,_C,_D] ;
false.

即:第一个答案不仅意外,而且还有第二个答案,其长度与原始答案相同。另一方面,第一个答案包括预期的解决方案。所以该计划过于笼统。

减小输入大小

?- delete(4,[4],L).
L = [] ;
L = [_A] ;
false.

第一个答案似乎现在正确,但第二个答案完全出乎意料。也就是说,delete(4,[4],[any])

所见证的定义过于笼统

专业化程序

要本地化该计划,请尽可能通过引入 false = 等目标来专门化该计划,并且只要delete(4,[4],[any])成功。我想出来了:

?- delete(4,[4],[any]).

delete(X,[Y|Ys],Zs) :- false, X = Y, delete(X,Ys,Zs).
delete(X,[_X|Ys],[_Z|Zs]) :- X = 4, _X = 4, _Z = any,
   delete(X,Ys,Zs).
delete(_X,[],[]) :- _X = 4.

现在显而易见的是,在此规则中,_X =4, _Z = any应该相同,X = 4, _X = 4应该是不同的。不平等最好用dif/2表示。

delete(_X, [], []).
delete(X, [Y|Ys], [Y|Zs]) :-
   dif(X, Y),
   delete(X, Ys, Zs).
delete(X, [X|Ys], Zs) :-
   delete(X, Ys, Zs).

现在可以通过多种方式使用此定义。像

?- delete(X,Xs,[1,2,3]).
Xs = [1, 2, 3],
dif(X, 3),
dif(X, 2),
dif(X, 1) ;
Xs = [1, 2, 3, X],
dif(X, 3),
dif(X, 2),
dif(X, 1) ;
Xs = [1, 2, 3, X, X],
dif(X, 3),
dif(X, 2),
dif(X, 1) ...

请注意,现在有无限多的答案!

答案 1 :(得分:4)

这个答案的灵感来自逻辑纯代码 @false提出in his answer

让我们使用元谓词tfilter/3 和术语不等式dif/3并简单地写:

?- Vs0 = [1,2,3,4], tfilter(dif(4),Vs0,Vs).
Vs0 = [1,2,3,4],
Vs  = [1,2,3  ].                      % succeeds deterministically

?- Vs0 = [1,2,3,4,2,3,4], tfilter(dif(2),Vs0,Vs).
Vs0 = [1,2,3,4,2,3,4],
Vs  = [1,  3,4,  3,4].                % succeeds deterministically

delete/3的实施归结为:

delete(E,Vs0,Vs) :-
   tfilter(dif(E),Vs0,Vs).

与@ false的代码一样,此实现是单调,这使得谓词具有多样性,使您能够获得逻辑上合理的答案 即使在处理非基础条款时也是如此。

最后,让我们有一个非常一般的查询并查看所有答案:

?- Vs0 = [X,Y,Z], delete(E,Vs0,Vs).
Vs0 = [X,Y,Z],     E=X ,     E=Y ,     E=Z , Vs = [     ] ;
Vs0 = [X,Y,Z],     E=X ,     E=Y , dif(E,Z), Vs = [    Z] ;
Vs0 = [X,Y,Z],     E=X , dif(E,Y),     E=Z , Vs = [  Y  ] ;
Vs0 = [X,Y,Z],     E=X , dif(E,Y), dif(E,Z), Vs = [  Y,Z] ;
Vs0 = [X,Y,Z], dif(E,X),     E=Y ,     E=Z , Vs = [X    ] ;
Vs0 = [X,Y,Z], dif(E,X),     E=Y , dif(E,Z), Vs = [X,  Z] ;
Vs0 = [X,Y,Z], dif(E,X), dif(E,Y),     E=Z , Vs = [X,Y  ] ;
Vs0 = [X,Y,Z], dif(E,X), dif(E,Y), dif(E,Z), Vs = [X,Y,Z].

答案 2 :(得分:2)

摆脱神秘变量名称的一种神秘方式是使用numbervars/3,例如,

?- length(X, 3).
X = [_G2019, _G2022, _G2025].

?- length(X, 3), numbervars(X, 0, _).
X = [A, B, C].

现在转到delete/3。除了其他小问题,如参数的异常顺序,子句的异常顺序等,你有一个主要问题:在第二个子句中,你将一个新的匿名变量放在原始列表的元素应该是的位置。

delete(X, [Y|Ys], [Y|Zs]) :- delete(X, Ys, Zs).

在第二个条款中,它有点有效:

?- delete(4, [1,2,3,4], L).
L = [1, 2, 3] .

?- delete(2, [1,2,3,4,2,3,4], L).
L = [1, 3, 4, 3, 4] .

您的实施还存在其他问题。您可以在SWI-Prolog的library(lists)中查看同一谓词的implementation:阅读文档!