谓词选择列表中的元素两次而不是更多

时间:2015-06-08 14:47:08

标签: prolog

我尝试编写一个谓词twice(El,L),当true.列表中的El时,它会返回twice(El,L) :- select(El,L,L1), member(El,L1), \+ twice(El,L1). 两次。这就是我所拥有的:

twice(2,[1,2,2,3,4])

适用于twice(X,[1,1,2,2,3,3]) 但对于X = 1 ; X = 1 ; X = 2...,它会使每个数字加倍{{1}}如果不使用任何累加器,我怎么能避免这种情况?

4 个答案:

答案 0 :(得分:5)

您想要描述一系列元素。为此,Prolog中有一种称为Definite Clause Grammars的特殊形式。在使用形式主义之前,让我们试着弄清楚E恰好出现两次的序列如何:

  1. 首先,是一个可能为空的序列,不包含E
  2. 然后,有一次E
  3. 然后又是一个没有E
  4. 的空序列
  5. 然后,第二次出现E
  6. 然后再一个可能没有E的空序列。
  7. 现在,将其纳入DCG形式主义

    twice(E, L) :-
       phrase(twice_occurring(E), L).  % Interface
    
    twice_occurring(E) -->
       seq_without(E),    % 1.
       [E],               % 2.
       seq_without(E),    % 3.
       [E],               % 4.
       seq_without(E).    % 5.
    
    seq_without(_E) -->
       [].
    seq_without(E) -->
       [X],
       {dif(X,E)},
       seq_without(E).
    

    或者,通过使用all//1并避免辅助定义更紧凑:

    twice(E, L) :-
        phrase(( all(dif(E)), [E], all(dif(E)), [E], all(dif(E)) ), L).
    

    这些定义基本上只有一个缺点:在当前系统中,它们没有得到最佳实现。如果您想了解更多信息,请参阅this

答案 1 :(得分:4)

使用if_/3和保持逻辑上纯粹且高效 (=)/3来自@false。它是这样的:

list_member1x([X|Xs],E) :-
   if_(X=E, maplist(dif(E),Xs), list_member1x(Xs,E)).

list_member2x([X|Xs],E) :-
   if_(X=E, list_member1x(Xs,E), list_member2x(Xs,E)).

twice(E,Xs) :-
   list_member2x(Xs,E).

那就是它。让我们进行一些查询!

?- twice(E,[1,2,3,4,5,2,3,4]).
E = 2 ;
E = 3 ;
E = 4 ;
false.

现在更普遍一些:

?- twice(X,[A,B,C,D]).
    A=X ,     B=X , dif(C,X), dif(D,X) ;
    A=X , dif(B,X),     C=X , dif(D,X) ;
    A=X , dif(B,X), dif(C,X),     D=X  ;
dif(A,X),     B=X ,     C=X , dif(D,X) ;
dif(A,X),     B=X , dif(C,X),     D=X  ;
dif(A,X), dif(B,X),     C=X ,     D=X  ;
false.

以下是OP提供的查询:

?- twice(2,[1,2,2,3,4]).
true.

?- twice(E,[1,1,2,2,3,3]).
E = 1 ;
E = 2 ;
E = 3 ;
false.

修改

作为替代方案,请将 tcount/3(=)/3结合使用,如下所示:

twice(E,Xs) :- tcount(=(E),Xs,2).

答案 2 :(得分:2)

尝试:

twice(E,L) :- 
    append(B1,[E|A1],L), 
    \+ member(E,B1), 
    append(B2,[E|A2],A1), 
    \+ member(E,B2), 
    \+ member(E,A2).

<强>附录

如果数字列表可以(部分)未绑定,则以下变体解决了问题。它使用“dif”而不是“\ =”,“+”。此外,它是一些优化(“附加”和“成员”已加入一个“appendchk”):

appendchk(L,L).
appendchk([E|Q2],[H|R]) :-
    dif(H,E),
    appendchk([E|Q2],R).

notmember(_,[]).
notmember(X,[H|Q]) :-
    dif(X,H),
    notmember(X,Q).

twice(E,L) :- 
    appendchk([E|A1],L), 
    appendchk([E|A2],A1), 
    notmember(E,A2).

示例:

twice(1,[1,2,3,4,2,3,2]).
false

twice(2,[1,2,3,4,2,3,2]).
false

twice(3,[1,2,3,4,2,3,2]).
true

twice(X,[1,2,3,4,2,3,2]).
X = 3
false

twice(X,[A,B]).
A = B, B = X

twice(X,[A,B,C]).
A = B, B = X,
dif(X, C)
A = C, C = X,
dif(B, X)
B = C, C = X,
dif(A, X)

答案 3 :(得分:1)

以下是我们如何声明礼貌图书馆(aggregate)所需的约束条件:

twice(El, L) :-
    aggregate(count, P^nth1(P,L,El), 2).

列表&#39;元素仅限于整数,library(clpfd) reification hint提供另一种解决方案:

twice(El, L) :- vs_n_num(L,El,2).
    % aggregate(count, P^nth1(P,L,El), 2).