Prolog:以意外顺序评估的规则

时间:2013-11-23 21:36:44

标签: list prolog rule

我目前正在开发Prolog程序,以从列表中删除重复项。

这是我的代码:

makeset([First], [First]). % if only one item in list, the solution is itself.
makeset([First, Second], [First, Second]) :-
   dif(First, Second).
makeset([First, Second], [First]) :-
    First = Second.
% if more than two items in list
makeset([First | Tail], FinalList) :-
   member(First, Tail),
   makeset(Tail, FinalList).
makeset([First | Tail], FinalList) :-
   not(member(First, Tail)),
   makeset(Tail, [First |FinalList]).

但是,当我在包含两个以上项目的列表上尝试此操作时,它只返回false。我跑了一个跟踪,发现它继续使用最后两个规则,即使它在列表中的两个项目。这是为什么?

示例查询:makeset([3, 2, 4, 5, 1], X). 我得回假,但每次评估都会使用最后一条规则。

修改 我将规则更改为以下内容,但仍然无法正确评估,因此问题必须与最后一条规则有关。

makeset([First], FinalList) :- 
 FinalList = [First]. % if only one item in list, the solution is itself.
makeset([First, Second], FinalList) :- 
 not(First = Second), FinalList = [First, Second].
makeset([First, Second], FinalList) :- 
 First = Second, FinalList = [First].
% if more than two items in list
makeset([First | Tail], FinalList) :- 
 member(First, Tail), makeset(Tail, FinalList).
makeset([First | Tail], FinalList) :- 
 not(member(First, Tail)), makeset(Tail, [First |FinalList]).

我如何修复最终规则?

3 个答案:

答案 0 :(得分:2)

净化您的代码!

首先关闭。看来你正在坚持这个世界的纯粹一面。当然,您正在使用dif/2!与其程序对手相比,其丑陋的兄弟(\=)/2意味着在这个时间点不能统一,但可能更晚,或从不,不要问我dif/2谓词具有纯粹的陈述含义。它意味着(句法或理论上的)不平等。这不仅会给你带来业力和嗜好。不,它甚至可以帮助您调试程序。事实上,我们可以使用Prolog来帮助我们找到程序中的问题 - 如果只有你的代码足够纯粹!首先,让我们净化你的代码,然后我们就可以调试它。

代码中唯一不正常的是not(member(First, Tail))。我将用maplist(dif(First),Tail)替换它。现在我们有一个纯粹的程序。它像以前一样失败了。首先可能是减少列表的大小。你真的想要完成那件苦差事吗?这是一个更简单的方法,只适用于纯Prolog程序。我会问:

  

告诉我所有可以想象的列表,他们的设置是什么。

是的,所有列表。不会有人留下。

询问Prolog!

| ?- length(L,I), makeset(L, X).
I = 1, L = [_A], X = [_A] ? ;
I = 2, L = [_A,_B], X = [_A,_B], prolog:dif(_A,_B) ? ;
I = 2, L = [_A,_A], X = [_A] ? ;
I = 2, L = [_A,_A], X = [_A] ? ...

这开始很糟糕!首先,I = 0没有解决方案。让我们添加事实makeset([], []),然后I = 2有这种冗余。您可以删除规则makeset([First, Second], [First]) :- First = Second.现在,让我们再次询问查询:

| ?- length(L,I), makeset(L, X).
I = 0, L = [], X = [] ? ;
I = 1, L = [_A], X = [_A] ? ;
I = 2, L = [_A,_B], X = [_A,_B], prolog:dif(_A,_B) ? ;
I = 2, L = [_A,_A], X = [_A] ? ;
I = 3, L = [_A,_A,_B], X = [_A,_B], prolog:dif(_A,_B) ? ;
I = 3, L = [_A,_A,_A], X = [_A] ? ;
I = 3, L = [_A,_B,_A], X = [_B,_A], prolog:dif(_B,_A) ? ;
I = 3, L = [_A,_A,_A], X = [_A] ? ;
I = 4, L = [_A,_A,_A,_B], X = [_A,_B], prolog:dif(_A,_B) ? ...

I in 0..2的所有答案现在看来都很完美。它们包含可以想象的所有列表。他们都是。请注意,在一个答案中再次出现dif/2,确保元素不同。

I = 3事情变得更加复杂。一些观察:

  • X = [_,_,_]没有答案。发现错误!事实上,发现了最小的错误!

  • L = [_A,_A,_A]

  • 有一个冗余答案

我们可以将第一个问题减少到一个目标:

| ?- L = [_,_,_], L = X, makeset(L, X).
no

应该有一个答案。我们现在可以采用另一种策略:

概括您的代码

显然makeset/2的定义过于专业化。我现在将尝试概括它以更好地本地化错误。为此,请将以下内容添加到您的程序中:

:- op(920,fy, [*]).

*_.

通过这种方式,我们可以在目标前添加*来删除它。我们也可以使用注释,但这是一种更简洁的方法。所以我现在要做的是系统地添加*,然后看看目标是否仍然失败。此外,我将一些变量重命名为新名称(_x),这相当于进一步概括。经过一番尝试,我最终得到了:

:- op(920,fy, [*]).
*_.

makeset([], []).
makeset([First], [_xFirst]).
makeset([First, Second], [_xFirst, _xSecond]) :-
   * dif(First, Second).
makeset([First | Tail], FinalList) :-
   * member(First, Tail),
   makeset(Tail, FinalList).
makeset([First | Tail], FinalList) :-
   * maplist(dif(First),Tail),
   makeset(Tail, [_xFirst |FinalList]).

?- L = [_,_,_], L = X, makeset(L, X).

在这个程序中,实际元素不再相关。因此它只是列表的长度!所以问题必须与处理长度的方式有关。如果存在第二个参数为更长列表的解决方案,则最后一条规则仅适用。哪个不可以。所以问题是最后一条规则。该清单应该在头部发生。

现在,出现了新的裁员。最终定义如下:

makeset([], []).
makeset([First | Tail], FinalList) :-
   member(First, Tail),
   makeset(Tail, FinalList).
makeset([First | Tail], [First|FinalList]) :-
   maplist(dif(First),Tail),
   makeset(Tail, FinalList).

有更聪明的方式来表达这种member / maplist dif二分法。但我会这样离开......

答案 1 :(得分:1)

在最后一条规则中,您将元素从第一个列表移动到第二个列表。 然后你会阻止匹配最高规则。

注意:dif / 2它是一个强大但复杂的谓词。你有效地需要它的力量吗?

答案 2 :(得分:1)

makeset([First | Tail], [First | FinalList]) :- 
 not(member(First, Tail)), makeset(Tail, FinalList).

您不应将First部分输入makeset,这会阻止先前的规则匹配。在使用First谓词统一FinalList后,您只应将makeset添加到列表中。

当输入列表为空时,您的解决方案也无法处理。