谓词选择列表中两次的元素不少于不多
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
恰好出现两次的序列看起来像:
- 首先,是一个可能为空的序列,它不包含
E
- 那么,
E
出现1次
- 然后又是一个没有
E
的可能为空的序列
- 然后,出现第二次
E
- 然后又是一个可能为空的序列,没有
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).
我正在尝试编写一个谓词 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
恰好出现两次的序列看起来像:
- 首先,是一个可能为空的序列,它不包含
E
- 那么,
E
出现1次
- 然后又是一个没有
E
的可能为空的序列
- 然后,出现第二次
E
- 然后又是一个可能为空的序列,没有
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).