谓词选择列表中两次的元素不少于不多

Predicate that pick elements which are on list twice not less not more

我正在尝试编写一个谓词 twice(El,L),当 El 在列表中恰好出现两次时,它将 return true.。这是我拥有的:

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... 我怎样才能在不使用任何累加器的情况下避免这种情况?

通过使用 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).

尝试:

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

附录

如果数字列表可以(部分)解除绑定,则以下变体可以解决问题。它使用 "dif" 代替“\=”、“+”。此外,它是一些优化("append"和"member"已加入单个"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)

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

  1. 首先,是一个可能为空的序列,它不包含 E
  2. 那么,E
  3. 出现1次
  4. 然后又是一个没有 E
  5. 的可能为空的序列
  6. 然后,出现第二次E
  7. 然后又是一个可能为空的序列,没有 E

现在,将其放入 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

这里是我们可以声明的方式,礼貌库(aggregate),所需的约束:

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

其中列表的元素仅限于整数,library(clpfd) reification hint 提供另一个解决方案:

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