【问题标题】:Prolog removing unique elements onlyProlog 仅删除唯一元素
【发布时间】:2014-03-25 03:04:57
【问题描述】:

我想返回一个删除所有唯一元素的列表,例如

remUniqueVals([1,1,2,2,3,4,4,5,6,6,6],Q).   
Q = [1,1,2,2,4,4,6,6,6].  

我的问题是目前我有返回的代码

remUniqueVals([1,1,2,2,3,4,4,5,6,6,6],Q).  
Q = [1, 2, 4, 6, 6].

因此只返回这些非唯一值的第一个实例。 这是我的代码:

remUniqueVals([], []).  
remUniqueVals([Q1|RestQ],[Q1|Xs]) :-        
   member(Q1,RestQ),  
   remUniqueVals(RestQ,Xs).  
remUniqueVals([Q1|RestQ],Xs) :-  
   remove(Q1,[Q1|RestQ], NewQ),  
   remUniqueVals(NewQ,Xs).  

我可以看到member(Q1,RestQ) 在第二次检查 1、2、4 时失败,因为它们现在不再在列表中,因此将它们删除。我想要一些帮助解决这个问题,我的想法是检查member(Q1, PreviousQ),这是已经在最终Q 中的元素。不知道如何去实施,尽管任何帮助将不胜感激。

更新:

好的,感谢您最后提出的建议:

remUniqueVals(_,[], []).  
remUniqueVals(_,[Q1|RestQ],[Q1|Xs]) :-        
   member(Q1,RestQ), 
   remUniqueVals(Q1,RestQ,Xs).  
remUniqueVals(PrevQ,[Q1|RestQ],[Q1|Xs]) :-        
   Q1 = PrevQ, 
   remUniqueVals(PrevQ,RestQ,Xs).  
remUniqueVals(PrevQ,[_|RestQ],Xs) :-  
   remUniqueVals(PrevQ,RestQ,Xs). 

remUniqueVals(0,[4,1,1,3,2,2,5,5],Q).
Q = [1, 1, 2, 2, 5, 5].

remUniqueVals(0, [A,B,C], [1,1]).
A = 1,
B = 1,
C = 1.

【问题讨论】:

  • 我怀疑你得到的答案不止一个。至少,你有六倍的三倍......
  • 我不确定我理解你的意思……三个 6 不是一回事。为了把它放到上下文中,我正在查看元组,例如上面可能是 [[a,6],[b,6],[c,6]]。我想删除最后一个元素唯一的所有元组。
  • 对于remUniqueVals([6,6,6],Q)member(E, [6,6]) 将有两个答案 - 但您不要报告它们。
  • 我认为我的问题不是他们没有被报告,而是只有两个答案。所以它检查成员(6,[6,6])。 (统一 Q = [6])然后检查成员(6,[6])。 (Q = [6,6]) 然后检查成员(6,[])。这失败了,因此返回 Q = [6,6]。这是我的问题。我想返回 Q = [6,6,6] 但不确定如何使用这种方法进行此操作。
  • 是否要求保留剩余元素的原始顺序?

标签: list prolog


【解决方案1】:

Prolog 规则是相互独立读取的,因此您需要一个规则来处理元素唯一和不唯一的情况。如果元素的顺序不相关,您可以使用:

?- remUniqueVals([A,B,C], [1,1]).
A = B, B = 1,
dif(C, 1) ;
A = C, C = 1,
dif(B, 1),
dif(B, 1) ;
B = C, C = 1,
dif(A, 1),
dif(A, 1) ;
false.

?- remUniqueVals([1,1,2,2,3,4,4,5,6,6,6],Q).
Q = [1, 1, 2, 2, 4, 4, 6, 6, 6] ;
false.

remUniqueVals([], []).
remUniqueVals([Q1|RestQ],[Q1|Xs0]) :-
   memberd(Q1, RestQ),
   phrase(delall(Q1, RestQ, NewQ), Xs0, Xs),
   remUniqueVals(NewQ, Xs).
remUniqueVals([Q1|RestQ],Xs) :-
   maplist(dif(Q1), RestQ),
   remUniqueVals(RestQ,Xs).

memberd(X, [X|_Xs]).
memberd(X, [Y|Xs]) :-
   dif(X,Y),
   memberd(X, Xs).

delall(_X, [], []) --> [].
delall(X, [X|Xs], Ys) -->
   [X],
   delall(X, Xs, Ys).
delall(X, [Y|Xs], [Y|Ys]) -->
   {dif(X,Y)},
   delall(X, Xs, Ys).

这是memberd/2 的替代定义,使用if_/3 可能更有效:

memberd(E, [X|Xs]) :-
   if_(E = X, true, memberd(E, Xs) ).

【讨论】:

  • 好的,谢谢!看起来它可能会起到作用,我需要一些时间来查看并首先了解它。
【解决方案2】:

这类似于原始解决方案,但它收集辅助列表中的非唯一值并检查它以避免从原始列表中删除最后一个:

remove_uniq_vals(L, R) :-
    remove_uniq_vals(L, [], R).

remove_uniq_vals([], _, []).
remove_uniq_vals([X|T], A, R) :-
    (   member(X, A)
    ->  R = [X|T1], A1 = A
    ;   member(X, T)
    ->  R = [X|T1], A1 = [X|A]
    ;   R = T1, A1 = A
    ),
    remove_uniq_vals(T, A1, T1).

测试...

| ?- remove_uniq_vals([1,2,3,1,2,3,1,2,3,4,3], Q).

Q = [1,2,3,1,2,3,1,2,3,3]

(1 ms) yes
| ?- remove_uniq_vals([1,1,2,2,3,4,4,5,6,6,6], Q).

Q = [1,1,2,2,4,4,6,6,6]

yes

因此,如果第一个参数是输入,谓词就可以很好地工作,并且它保持列表中其余元素的原始顺序。

然而,这个谓词不是完全关系的,因为它会失败,其中第一个参数是一个已知数量的元素的未实例化列表,而第二个参数是一个不同的列表固定数量的元素。所以这样的事情会起作用:

| ?- remove_uniq_vals([A,B,C], L).

B = A
C = A
L = [A,A,A]

(1 ms) yes

但类似以下的失败:

| ?- remove_uniq_vals([A,B,C], [1,1]).

no

【讨论】:

  • 谢谢你,这就是我要找的。​​span>
【解决方案3】:

这是另一个受@CapelliC 解决方案启发的纯关系解决方案。这个现在保留了重复的顺序。有趣的是,@CapelliC 的解决方案中发生的隐式量化现在必须显式完成。

拥有纯粹的关系定义的最大优点是没有就是没有。是的,是的。那就是:你不必担心你得到的答案是否正确。它是正确的(或不正确的——但不是部分正确的)。如果方法失败,通常可以通过生成instantiation_error 来清理非关系解决方案。但正如您可以验证自己的那样,两者都“忘记”了此类测试,从而为错误准备了一个良好的栖息地。对其他解决方案的安全测试应该是 ground(Xs)ground(Xs), acyclic_term(Xs),但很多时候这被认为过于受限。

remUniqueVals2(Xs, Ys) :-
   tfilter(list_withduplicate_truth(Xs),Xs,Ys).

list_withduplicate_truth(L, E, Truth) :-
   phrase(
      (  all(dif(E)),
         (  {Truth = false}
         |  [E],
            all(dif(E)),
            (   {Truth = false}
            |   {Truth = true},
                [E],
                ...
            )
         )
      ),  L).

all(_) --> [].
all(P_1) -->
   [E],
   {call(P_1,E)},
   all(P_1).

... --> [] | [_], ... .

tfilter(     _, [], []).
tfilter(TFilter_2, [E|Es], Fs0) :-
   call(TFilter_2,E,Truth),
   (  Truth = false,
      Fs0 = Fs
   ;  Truth = true,
      Fs0 = [E|Fs]
   ),
   tfilter(TFilter_2, Es, Fs).

使用if_/3的另一种更紧凑的方式

tfilter(   _, [], []).
tfilter(TFilter_2, [E|Es], Fs0) :-
   if_(call(TFilter_2,E), Fs0 = [E|Fs], Fs0 = Fs ),
   tfilter(TFilter_2, Es, Fs).

【讨论】:

  • 感谢 (+1)。这是一个听起来很天真的谓词,但对于纯粹的关系解决方案来说似乎特别具有挑战性。
  • @mbratch:净化你的方法怎么样? (提出一个新的、单独的答案)。
  • 我一直在花时间研究它。在愚弄了几个失败的想法之后,目前看起来很有挑战性...... :)
【解决方案4】:

这是@mbratch 解决方案的纯化版本。它使用member/2 的改进版本,没有像member(X,[a,a]) 这样的冗余答案。

memberd_truth_dcg(X, Xs, Truth) :-
   phrase(( all(dif(X)), ( [X], {Truth = true}, ... | {Truth = false} ) ), Xs).

一个稍微概括的版本,只需要有一个列表前缀,而不需要一个列表:

memberd_truth(_X, [], false).
memberd_truth(X, [X|_], true).
memberd_truth(X, [Y|Ys], Truth) :-
   dif(X,Y),
   memberd_truth(X, Ys, Truth).

变量的命名方式与@mbratch 的解决方案相同:

remove_uniq_valsBR(L, R) :-
   remove_uniq_valsBR(L, [], R).

remove_uniq_valsBR([], _, []).
remove_uniq_valsBR([X|T], A, R) :-
    memberd_truth(X, A, MemT1),
    (  MemT1 = true,
       R = [X|T1], A1 = A
    ;  MemT1 = false,
       memberd_truth(X, T, MemT2),
       (  MemT2 = true,
          R = [X|T1], A1 = [X|A]
       ;  MemT2 = false,
          R = T1, A1 = A
       )
    ),
    remove_uniq_valsBR(T, A1, T1).

更简洁地使用if/3:

remove_uniq_valsBR([], _, []).
remove_uniq_valsBR([X|T], A, R) :-
    if_( memberd_truth(X, A),
       ( R = [X|T1], A1 = A ),
       if_( memberd_truth(X, T),
          ( R = [X|T1], A1 = [X|A] ),
          ( R = T1, A1 = A ) ) )
    ),
    remove_uniq_valsBR(T, A1, T1).

我不喜欢很多多余的dif/2 约束。我希望这个版本会少一些:

| ?- length(L,_),remove_uniq_valsBR(L,L).
L = [] ? ;
L = [_A,_A] ? ;
L = [_A,_A,_A] ? ;
L = [_A,_A,_A,_A] ? ;
L = [_A,_A,_B,_B],
dif(_B,_A) ? ;
L = [_A,_B,_A,_B],
dif(_A,_B),
dif(_B,_A),
dif(_B,_A),
dif(_A,_B) ? ...

当然可以检查dif/2 是否已经存在,但我更喜欢从一开始就发布较少dif/2 目标的版本。

【讨论】:

    【解决方案5】:

    保留!基于if_/3(=)/3tpartition/4,我们定义:

    remUniqueValues([], [])。
    remUniqueValues([X|Xs1], Ys1) :-
       tpartition(=(X), Xs1, Eqs, Xs0),
       if_(Eqs = [],
           Ys1 = Ys0,
           append([X|Eqs], Ys0, Ys1)),
       remUniqueValues(Xs0, Ys0)。
    

    让我们看看它的实际效果!

    ?- remUniqueValues([A,B,C], [1,1])。 A=1 , B=1 , 差异(C,1) ; A=1,差异(B,1),C=1 ;差异(A,1),B=1,C=1 ;错误的。 ?- remUniqueValues([1,1,2,2,3,4,4,5,6,6,6], Vs)。 Vs = [1,1,2,2,4,4,6,6,6]。 % 确定性地成功

    【讨论】:

    • 您是否可以编辑逻辑纯度标签信息并解释这种“逻辑纯度”的含义,因为正如现在所写的那样(“仅使用 Horn 子句的程序”) 我看不出if_/3 是如何限定的,尽可能多地使用剪辑,以及各种元逻辑(正确的术语是什么?var/1 等)谓词,即低级的东西.我知道它实现了一些“纯粹”的效果,我只想看到它被解释,所以我(可能还有其他人)不必猜测。非常感谢。 -- 那么,if_/3 是如何成为逻辑纯的呢?
    • @WillNess:最好是问这个问题。
    【解决方案6】:

    基于 3 个内置函数的解决方案:

    remUniqueVals(Es, NUs) :-
        findall(E, (select(E, Es, R), memberchk(E, R)), NUs).
    

    可以读作

    找到所有在被选中后仍然出现在列表中的元素

    【讨论】:

    • 不错,但不相关。例如,remUniqueValsCC([A,B],[]). 失败
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-05-23
    • 2015-01-08
    • 1970-01-01
    • 2018-11-05
    • 2016-02-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多