使用Prolog删除列表中的相邻重复项

时间:2016-04-11 18:35:03

标签: prolog

我发现了一个类似的帖子,但它对我不起作用。所以请不要试图将我重定向到其他链接。

这是我想要的结果:

removeadj([a,a,a,b,c,c,a,a],[a,b,c,a]).
>True.

我试过这段代码:

concatener([],R,R). 
concatener([X|Y],L,R):-concatener(Y,L,R1),R=[X|R1].

removeadj([],[]).
removeadj([X],[X]).
removeadj([X|Y],R):- Y=[X|L],removeadj(Y,R1),concatener([],R1,R).
removeadj([X|Y],R):- removeadj(Y,R1),concatener(X,R1,R).

当我尝试使用一个元素重复多次的列表时,它可以工作:

removeadj([a,a,a,a,a],[a]).
> True

但是当我使用不同的元素时,它不起作用:

removeadj([a,a,a,b],[a,b]). 
> False.

我没有看到问题所在,所以我无法修复它。我需要你的帮助。

3 个答案:

答案 0 :(得分:4)

关系名称

首先是重新考虑你的关系的名称。目前,它表明有人必须做某事。 removeadj这是一个命令。在编程语言中有足够的名称,其中命令是统治隐喻。但不是在Prolog中。

在Prolog,我们有关系。反映这种关系的名称通常非常有用。为什么不list_list/2?毕竟,你的关系是两个列表!好吧,也许这个名字有点过于笼统。那么list__list_without_adjacent_elements/2呢?冗长但有关系。也许我们将其缩短为:list_noadjs/2。注意最后的s:这意味着:它是复数,这意味着它是一个列表。

观察属性

在考虑“做”这个或那个之前。而是冥想具体的例子 - 最好是地面的例子,就像你给他们的一样。以及其他属性。一个观察结果是第二个列表中的所有元素都将存在于第一个列表中。事实上不仅如此。但它们也会以相同的顺序出现。让我试着制定一下。当然,这种观察不足以编写整个谓词。但是这里的Prolog很酷:我们不需要实现一切。我们可以从包含我们想要的所有内容以及更多内容的粗略概括开始。

从过于笼统的定义开始。

为了向您展示最极端,让我们试试:

list_noadjs(_Xs, _Ys).

这是所有二元关系的母亲!无论如何,这个定义总是成功的。显然,我们必须专注于它。比如说,通过查看列表的第二个参数:

list_noadjs(_Xs, []).
list_noadjs(_Xs, [Y|Ys]) :-
   list_noadjs(_, Ys).

如果列表是[]那么将是原始列表。两者都以相同的元素开始!

list_noadjs(Xs, []) :-
   Xs = [].
list_noadjs(Xs, [Y|Ys]) :-
   Xs = [Y|_],
   list_noadjs(_, Ys).

或更紧凑:

list_noadjs([], []).
list_noadjs([Y|_Xs], [Y|Ys]) :-
   list_noadjs(_, Ys).

现在,第一个列表包含第二个列表的元素。在其他事物之间:

list_noadjs([], []).
list_noadjs([Y|Xs0], [Y|Ys]) :-
   list_(Xs0,Xs1),
   list_noadjs(Xs1, Ys).

list_(Xs,Xs).
list_([_|Xs0],Xs) :-
   list_(Xs0,Xs).

这已经是我们的关系吗?我们试一试:

| ?- list_noadjs("aaab",Ys).
Ys = "aaab" ? ;
Ys = "aaa" ? ;
Ys = "aab" ? ;
Ys = "aa" ? ;
Ys = "aab" ? ;
Ys = "aa" ? ;
Xs = "ab" ? ;  % <===== * RRRIGHT !!!!***
Ys = "a" ? ;
no

(顺便说一下。我使用library(double_quotes)来提高答案的可读性。)

所以我们确实有了预期的解决方案。唉,还有很多不正确的解决方案!我们将不得不继续专攻这个计划:

list_noadjs([], []).
list_noadjs([Y|Xs0], [Y|Ys]) :-
   eq_list_(Y, Xs0,Xs1),
   list_noadjs(Xs1, Ys).

eq_list_(_, Xs,Xs).
eq_list_(Y, [Y|Xs0],Xs) :-
   eq_list_(Y, Xs0,Xs).

现在情况好多了,但仍然不完美:

| ?- list_noadjs("aaab",Ys).
Ys = "aaab" ? ;
Ys = "aab" ? ;
Ys = "aab" ? ;
Ys = "ab" ? ;  % !!! Right
no

我们必须进一步专门化该程序:在一系列相同的元素之后,必须有其他东西:

list_noadjs([], []).
list_noadjs([Y|Xs0], [Y|Ys]) :-
   eq_list_(Y, Xs0,Xs1),
   nohead(Xs1, Y),
   list_noadjs(Xs1, Ys).

eq_list_(_, Xs,Xs).
eq_list_(Y, [Y|Xs0],Xs) :-
   eq_list_(Y, Xs0,Xs).

nohead([], _X).
nohead([X|_], Y) :-
   dif(X, Y).

这就是我们的关系。

享受这种关系!

严重。不要只使用你拥有的测试用例。你现在有了关系!这不是伪装的功能,它确实不止于此。试试看!问完全不寻常的事情,比如:

| ?- list_noadjs(Xs,"abc").
Xs = "abc" ? ;
Xs = "abcc" ? ;
Xs = "abccc" ? ;
Xs = "abcccc" ...

所以我们在这里问:哪些列表对应"abc"?请注意,只重复c!所有其他解决方案都隐藏在这个无限的墙壁后面。但我们可以通过一些小技巧来获取它们:

| ?- length(Xs,N), list_noadjs(Xs,"abc").
Xs = "abc", N = 3 ? ;
Xs = "abcc", N = 4 ? ;
Xs = "abbc", N = 4 ? ;
Xs = "aabc", N = 4 ? ;
Xs = "abccc", N = 5 ? ;
Xs = "abbcc", N = 5 ? ;
Xs = "abbbc", N = 5 ? ;
Xs = "aabcc", N = 5 ? ;
Xs = "aabbc", N = 5 ? ;
Xs = "aaabc", N = 5 ? ;
Xs = "abcccc", N = 6 ? ...

不要回避违规行为。

我们已经看过:很多时候,我们会得到无数的解决方案。并且(我必须承认)更糟糕的是:有时,我们的关系甚至不会终止。

这是一个这样的例子。比方说,第二个参数中的列表是否包含重复项?或者说以下内容:

| ?- list_noadjs(Xs,[X,X]).
** LOOPS **

Prolog回答:嘟,,咕,,看见......

要掌握Prolog,您必须详细了解这一点。但目前,通常有一条好的出路:

专门查询以获得终止

所以不要问:是否任何术语可能与[X,X]对应我们可能会问:是否有大小为5的列表(或任何其他有限大小)。现在Prolog否认了这一点:

 | ?- Xs = [_,_,_,_,_] list_noadjs(Xs,[X,X]).
 no

这不是你想要的普遍答案。但它总比没有好。

有时候所有这些查询对你来说都太过分了。让Prolog通过以下方式为您考虑:

枚举所有解决方案

通常,这很简单。从最常见的查询开始。这里的最大优点是根本不需要思考。而且你看起来像个专业人士:

| ?- list_noadjs(Xs,Ys).
Xs = [], Ys = [] ? ;
Xs = [_A], Ys = [_A] ? ;
Xs = [_A,_B], Ys = [_A,_B], dif(_B,_A) ? ;
Xs = [_A,_B,_C], Ys = [_A,_B,_C], dif(_B,_A), dif(_C,_B) ? ;
Xs = [_A,_B,_C,_D], Ys = [_A,_B,_C,_D], dif(_B,_A), dif(_C,_B), dif(_D,_C) ? ;
Xs = [_A,_B,_C,_D,_E], Ys = [_A,_B,_C,_D,_E], dif(_B,_A), dif(_C,_B), dif(_D,_C),
...

我们在这里得到的是所谓的答案。单个答案可能包含无限许多解决方案。想一想:你正在寻找无限!某些条件(称为约束)必须保持,例如dif/2。但就是这样。

第三个答案是:

Xs = [_A,_B], Ys = [_A,_B], dif(_B,_A) ? ;

因此XsYs是具有两个不同元素的相同列表。所以这个答案意味着Xs = "ab", Ys = "ab",但Xs = [1,2], Ys = [1,2]还有很多甚至更多。

更好的是,以系统(“公平”)的方式列举所有答案:

| ?- length(Xs, N), list_noadjs(Xs,Ys).
Xs = [], N = 0, Ys = [] ? ;
Xs = [_A], N = 1, Ys = [_A] ? ;
Xs = [_A,_B], N = 2, Ys = [_A,_B], dif(_B,_A) ? ;
Xs = [_A,_A], N = 2, Ys = [_A] ? ;
Xs = [_A,_B,_C], N = 3, Ys = [_A,_B,_C], dif(_B,_A), dif(_C,_B) ? ;
Xs = [_A,_B,_B], N = 3, Ys = [_A,_B], dif(_B,_A) ? ;
Xs = [_A,_A,_B], N = 3, Ys = [_A,_B], dif(_B,_A) ? ;
Xs = [_A,_A,_A], N = 3, Ys = [_A] ? ;
Xs = [_A,_B,_C,_D], N = 4, Ys = [_A,_B,_C,_D], dif(_B,_A), dif(_C,_B), dif(_D,_C) ?
...

这些所有解决方案最长为3级。没有其他的!我们肯定知道这一点,因为最后的答案已经是4号了。所以下面的所有解决方案都已经在这里了!

经常看这些答案非常有帮助。例如,它允许您快速检测错误(如之前给出的其他答案)。所以,不要告诉任何人这个伎俩。

答案 1 :(得分:1)

谓词中的最后一个句子仍有问题:

removeadj([X|Y], [X|R1]):- removeadj(Y, R1).

如果X后面的元素也是X,则X仍会在第二个参数的头部中携带。在允许第二个参数之前,此子句必须检查以下元素是否不同:

removeadj([X,Y|L], [X|R]) :-
    dif(X,Y),
    removeadj([Y|L], R).

此处,Y仅在与X不同的情况下位于第二个列表的开头。

所以整个解决方案看起来像:

removeadj([], []).
removeadj([X], [X]).
removeadj([X,X|T], R) :-       % drop an X if next element is X
    removeadj([X|T], R).
removeadj([X,Y|T], [X|R]) :-   % carry X along if next element different
    dif(X,Y),
    removeadj([Y|T], R).

答案 2 :(得分:0)

removeadj([],[]).
removeadj([X],[X]).
removeadj([X|Y],R):- Y=[X|L],removeadj(Y,R1),R=R1.
removeadj([X|Y],[X|R1]):- removeadj(Y,R1).