为什么我在prolog中的`length / 2`实现中得到重复的结果?

时间:2014-09-26 13:27:59

标签: prolog

我正在Prolog练习。我已经实现了类似于length/2的谓词(此处称为ue_length),如下所示:

%%
% ue_length/2
%%

% not a list predicate

\+(T) :- call(T), !, fail.
\+(_).

% target case

ue_length(List, Length) :- \+(is_list(List)), !, fail.
ue_length(List, Length) :- ue_length(List, 0, Length).

% standard cases

ue_length([], Length, Length).
ue_length([X], Part, Length) :- ue_length([], [Part], Length).
ue_length([X| Rest], Part, Length) :- ue_length(Rest, [Part], Length).

结果应该是一个术语而不是一个数字:0[][0]为长度为1且[...[0]...]的列表n }括号)获取长度为n的列表。

当我用例如Prolog(SWI-Prolog 6)查询时[1,2,3,4,5]我得到了两次正确的结果。

?- ue_length([1,2,3,4,5], X).
X = [[[[[0]]]]] 
X = [[[[[0]]]]].

我是Prolog的新手。有人可以解释为什么我会得到多余的结果吗?

1 个答案:

答案 0 :(得分:3)

尽量减少有问题的查询

首先,减小查询的大小。仅ue_length([1], X).就可以观察到相同的问题(冗余解决方案)。

但还有其他问题更多:

您的定义是否为关系?

?- ue_length(L,N).
false.

所以你的定义用列表[1]成功但是在它的位置失败了吗?这根本没有任何意义!即使循环也是一种更好的行为。

另一个有问题的案例是

?- ue_length(L,0).
false.

此处,您的定义应以L = []作为答案。

这是使用is_list/1的测试的罪魁祸首。只需删除规则

即可
ue_length(List, Length) :- \+(is_list(List)), !, fail. % incorrect!

现在,您的定义也可用于询问最常见的查询,其中包含参数中的不同变量。

?- ue_length(L,N).
  L = [], N = 0
; L = [_A], N = [0]
; L = [_A], N = [0]
; L = [_A, _B], N = [[0]]
...

这是Prolog的一个非常好的属性:您不需要为测试用例输入具体数据。只需像专业人士一样输入最常见的查询,Prolog将为您完成其余的工作。

使用 false

进行本地化

要将此冗余本地化,首先要考虑如何发生这种情况。一个简单的可能性是程序中的某些子句是冗余的,因此可以删除。 让我们说它是最后一个。因此,我会在最后一个条款中插入一个 false 目标。我再次尝试最常见的查询。唉......

ue_length(List, Length) :- ue_length(List, 0, Length).

% standard cases

ue_length([], Length, Length).
ue_length([_X], Part, Length) :-
   ue_length([], [Part], Length).
ue_length([_X| Rest], Part, Length) :-
   false,
   ue_length(Rest, [Part], Length).

?- ue_length(L,N).
  L = [], N = 0
; L = [_A], N = [0]
; false.

该规则必须非常重要,因为现在我们只获得长度为零和一的列表的答案。所以我猜错了。但我的观点是,只需使用最常用的查询,您就可以轻松地看到这一点。实际的冗余条款是另一个。并且不要忘记询问最一般的查询,以确保您能得到所有答案!