【问题标题】:Prolog: splitting a list into two lists (unique items / duplicate items)Prolog:将列表拆分为两个列表(唯一项/重复项)
【发布时间】:2013-04-06 11:13:44
【问题描述】:

我一直在尝试将给定列表拆分为两个不同的列表:唯一列表和重复列表。 例如,如果我们有列表[1, 1, 2, 3, 3, 4, 5],我希望唯一列表为[2, 4, 5],重复列表为[1, 3]。 我不希望列表中的所有 1 都在重复列表中。我只需要其中之一。 我现在的代码:

compareL([_|[]], Unique, Dup).    
compareL([X3,Y3 | Tail], [X3 | Unique], Dup) :-
    X3 =\= Y3,
    compareL([Y3 | Tail], Unique, Dup). 
compareL([X3,Y3 | Tail], Unique, [X3 | Dup]) :- 
    X3 = Y3,
    skipDups(X3, Tail, Unique, Dup).

skipDups(_, [], Unique, Dup).   
skipDups(X3,[Y3 | Tail], Unique, Dup) :- 
    X3 =\= Y3,
    compareL([Y3 | Tail], Unique, Dup).
skipDups(X3,[Y3 | Tail], Unique, Dup) :-
    X3 = Y3,
    skipDups(X3, Tail, Unique, Dup).

如果我运行compareL([1, 1, 2, 3, 3, 4, 5], Unique, Dup).,使用上面给出的示例列表,我得到:

Unique = [2, 4|_G1954],
Dup = [1, 3|_G1948].

我无法弄清楚为什么在两个列表的末尾我都会得到“_G1954”和“_G1948”。任何帮助,将不胜感激。谢谢。

【问题讨论】:

  • 代替compareL([_|[]], Unique, Dup). 试试compareL([_], [], []).
  • 谢谢。这摆脱了'_G1954'和'_G1948'。但是当我在列表末尾有两个 5 时,它又回来了。知道为什么吗?
  • 我认为你的谓词太复杂了......我会用替代代码发布答案。
  • 如果可以,那就太好了。谢谢!

标签: list prolog


【解决方案1】:

我们可以在 if_/3(=)/3tpartition/4 的基础上保留

list_uniqs_dups([],[],[]).
list_uniqs_dups([X|Xs0],Us0,Ds0) :-
    tpartition(=(X),Xs0,Es,Xs),
    if_(Es=[],
        Us0+Ds0=[X|Us]+Ds,
        Ds0+Us0=[X|Ds]+Us),
    list_uniqs_dups(Xs,Us,Ds).

这是 OP 给出的查询:

?- list_uniqs_dups([1,1,2,3,3,4,5],Us,Ds).
Ds = [1,3], Us = [2,4,5].                   % succeeds deterministically

好的!下面的相当一般查询怎么样?

?- list_uniqs_dups([],Us,Ds)。 Ds = [],我们 = []。 ?- list_uniqs_dups([A],Us,Ds)。 Ds = [],我们 = [A]。 ?- list_uniqs_dups([A,B],Us,Ds)。 Ds = [B],我们 = [],A=B ; Ds = [],Us = [A,B],差异(A,B)。 ?- list_uniqs_dups([A,B,C],Us,Ds)。 Ds = [C],Us = [],A=B,B=C ; Ds = [B],Us = [C],A=B,dif(B,C) ; Ds = [C],Us = [B],A=C,dif(B,C) ; Ds = [C],Us = [A],dif(A,C),B=C ; Ds = [],Us = [A,B,C],dif(A,B),dif(A,C),dif(B,C)。

【讨论】:

  • 我们需要一个通用的缩进规则,让if_/3 跨越几行。
  • @false。确切地!我也不喜欢X+Y=A+B
【解决方案2】:

使用(=)/3回答

list_uniqs_alldups(Es,Us,Ds) :
   tpartition(list_uniqmember_t(Es),Es,Us,Ds).

list_uniqmember_t(Es,X,T) :-
   tfilter(=(X),Es,Xs),
   =(Xs,[X],T).

示例查询:

?- list_uniqs_alldups([1,1,2,1,1,3,4,3],Us,Ds).
Us = [2, 4],
Ds = [1, 1, 1, 1, 3, 3].

?- list_uniqs_alldups([1,1,2,3,3,1,1,3,4,3],Us,Ds).
Us = [2, 4],
Ds = [1, 1, 3, 3, 1, 1, 3, 3].

?- list_uniqs_alldups([8,1,1,2,3,3,1,1,3,4,3],Us,Ds).
Us = [8, 2, 4],
Ds = [1, 1, 3, 3, 1, 1, 3, 3].

?- list_uniqs_alldups(X,Us,Ds).
X = Us, Us = Ds,   Ds = [] ;
X = Us, Us = [_A], Ds = [] ;
X = Ds, Us = [],   Ds = [_A,_A] ;
X = Ds, Us = [],   Ds = [_A,_A,_A] ;
...

对于运行长度编码,我使用了splitlistIfAdj/3

list_rle(List,Rle) :-
  splitlistIfAdj(dif,List,Rle0),
  maplist(rle_length,Rle0,Rle).

rle_length([H|T],RleLen) :-
  length([H|T],L),
  RleLen = L*H.

查询:

?- list_rle([a,a,b,a,a,c,d,c],X).
X = [2*a, 1*b, 2*a, 1*c, 1*d, 1*c].

我不确定如何更改它以使其双向工作。

你也可以这样做:

new_list_uniqs_alldups(List,Us,Ds):-
   list_rle(List,Rle),
   tpartition(singlelist_t,Rle,Us,Ds).

singlelist_t(L,T) :-
   L=N*_,
   if_(N=1,T=true,T=false).

示例 Q:

 ?- new_list_uniqs_alldups([1,1,2,1,1,3,4,1,1,7,8],U,D).
 U = [1*2, 1*3, 1*4, 1*7, 1*8],
 D = [2*1, 2*1, 2*1].

 ?- new_list_uniqs_alldups([7,7,7,2,1,1,3,4,1,1,7,8],U,D).
 U = [1*2, 1*3, 1*4, 1*7, 1*8],
 D = [3*7, 2*1, 2*1].

【讨论】:

  • 拍得好!但是,我希望看到一种实现,它可以使所有三个查询确定性地成功,而不会破坏逻辑含义的元逻辑内容(当与非常笼统的术语一起使用时)。
  • 这3个查询没问题!如果您仔细查看使用prolog-toplevel 得到的答案序列,就像swi-prolog 一样,您会发现它们看起来都像Us = ..., Ds = ... ; false.。尾随的; false. 表明Prolog 不知道是否存在其他答案。现在,我们知道在这些情况下没有。我假设这三个示例目标已充分实例化,因此如果我们使用合适的谓词定义,Prolog 也可以知道这一点。答案应该类似于Us = [...], Ds = [...].! OTOH 它在逻辑上仍然应该是纯粹的。更清晰?
  • 考虑查询?- list_uniqmember_t([1,2],X,T).。您之前的版本正确回答:X = 1,T = true ; X = 2,T = true ; T = false,dif(X, 2),dif(X, 1)。 “新”版的答案不完整X = 1,T = true
  • 类似于 cut 的 Prolog 构造 (!)/0(->)/2(*->)/2 轻松破解代码!我不想告诉你,但你刚刚破坏了你的代码......
  • 在这个答案中查看list_RLE/2stackoverflow.com/a/33180378/4609915
【解决方案3】:

这里有一个解决方案,键是 take/4 ,它消耗所有匹配的前导项目,从而可以轻松测试列表([_|_] 匹配至少 1 个元素的任何列表)

compareL([], [], []).
compareL([X|Xs], U, D) :-
    (   take(X, Xs, [_|_], Ys)
    ->  compareL(Ys, U, B), D = [X|B]
    ;   compareL(Xs, A, D), U = [X|A]
    ).

take(X, [X|Xs], [X|R], Ys) :-
    !, take(X, Xs, R, Ys).
take(_, Ys, [], Ys).

【讨论】:

  • 不使用->可以吗?
  • 有可能,但很笨拙:compareL([X|Xs], U, D) :- take(X, Xs, [_|_], Ys), !, compareL(Ys, U, B), D = [X|B] ; compareL(Xs, A, D), U = [X|A].(未经测试)
【解决方案4】:

你可以这样写:

split_seq([], [], []).

split_seq([H | T], L1_out, L2_out) :-
    split_seq(T, L1, L2),
    (   select(H, L1, L1_out)
    ->  (   member(H, L2)
        ->  L2_out = L2
        ;   L2_out = [H | L2])
    ;   L1_out = [H | L1],
        L2_out = L2).

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-20
    • 1970-01-01
    • 1970-01-01
    • 2012-12-04
    • 1970-01-01
    • 2015-03-03
    相关资源
    最近更新 更多