4

我正在尝试在 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

但是我在使用“删除”谓词时遇到了麻烦。以下是它的实现方式:

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 而不是数字呢?

提前谢谢了!

4

3 回答 3

4

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

坚持 Prolog 的纯单调子集

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

总是看所有的答案

Prolog 的顶级循环仅向您显示第一个答案。您必须按;或要求更多SPACE。您的程序带有(=)/2Gives(具有更易读的变量):

?- 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])成功。我想出:

?- 删除(4,[4],[任何])。

delete(X,[Y|Ys],Zs) :- false , X = Y, delete(X,Ys,Zs)。
删除(X,[_X|Ys],[_Z|Zs]):- X = 4,_X = 4,_Z = 任意,
   删除(X,Ys,Zs)。
删除(_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) ...

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

于 2014-11-20T12:01:13.833 回答
4

这个答案的灵感来自@false在他的答案中提出的逻辑纯代码 。

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

?- Vs0 = [1,2,3,4], tfilter(dif( 4 ),Vs0,Vs)。
Vs0 = [1,2,3, 4 ],
VS = [1,2,3]。% 确定性地成功

?- 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]。% 确定性地成功

实现delete/3归结为:

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

与@false 的代码一样,此实现是单调的,这使得谓词具有通用性,即使在使用非基本术语时也能让您获得逻辑上合理的答案。

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

?- Vs0 = [X,Y,Z],删除(E,Vs0,Vs)。
Vs0 = [ X , Y , Z ],      E = X ,      E = Y ,      E = Z , Vs = [ ] ;
Vs0 = [ X , Y , Z ],      E = X ,      E = Y , 差异 ( E , Z ), Vs = [     Z ] ;
Vs0 = [ X , Y , Z ],      E = X , dif( E , Y ),      E = Z , Vs = [   Y   ] ;
Vs0 = [ X , Y , Z ],      E = X , 差异 ( E , Y ), 差异 ( E , Z ), Vs = [   Y , Z ] ;
Vs0 = [ X , Y , Z ], 差异 ( E , X ),      E = Y ,      E = Z , Vs = [ X     ] ;
Vs0 = [ X , Y , Z ], 差异 ( E , X ),      E = Y , 差异 ( E , Z ), Vs = [ X ,   Z ] ;
Vs0 = [ X , Y , Z ], 差异 ( E , X ), 差异 ( E , Y ),      E = Z , Vs = [ X , Y   ] ;
Vs0 = [ X , Y , Z ], dif( E , X ), dif( E , Y ), dif( E , Z ), Vs = [ X , Y , Z ]。
于 2015-05-16T17:25:39.183 回答
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):也请阅读文档!

于 2014-10-25T09:32:31.797 回答