我目前正在开发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]).
我如何修复最终规则?
答案 0 :(得分:2)
首先关闭。看来你正在坚持这个世界的纯粹一面。当然,您正在使用dif/2
!与其程序对手相比,其丑陋的兄弟(\=)/2
意味着在这个时间点不能统一,但可能更晚,或从不,不要问我,dif/2
谓词具有纯粹的陈述含义。它意味着(句法或理论上的)不平等。这不仅会给你带来业力和嗜好。不,它甚至可以帮助您调试程序。事实上,我们可以使用Prolog来帮助我们找到程序中的问题 - 如果只有你的代码足够纯粹!首先,让我们净化你的代码,然后我们就可以调试它。
代码中唯一不正常的是not(member(First, Tail))
。我将用maplist(dif(First),Tail)
替换它。现在我们有一个纯粹的程序。它像以前一样失败了。首先可能是减少列表的大小。你真的想要完成那件苦差事吗?这是一个更简单的方法,只适用于纯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
添加到列表中。
当输入列表为空时,您的解决方案也无法处理。