List 元素在Prolog的列表中只出现一次

List 元素在Prolog的列表中只出现一次,list,prolog,predicate,prolog-dif,logical-purity,List,Prolog,Predicate,Prolog Dif,Logical Purity,我想写一个谓词,检查元素在列表中是否只出现一次 once(Element, List). 我的代码: once(X, [H | T]) :- \+ X = H, once(X, T). once(X, [X | T]) :- \+ member(X, T). ?- once(c, [b,a,a,c,b,a]). true ?- once(b, [b,a,a,c,b,a]). false. 但如果我问: once(X, [b,a,a,c,b,a]). 序言部分的答

我想写一个谓词,检查元素在列表中是否只出现一次

once(Element, List).
我的代码:

once(X, [H | T]) :-
    \+ X = H,
    once(X, T).
once(X, [X | T]) :-
    \+ member(X, T).

?- once(c, [b,a,a,c,b,a]).
true

?- once(b, [b,a,a,c,b,a]).
false.
但如果我问:

once(X, [b,a,a,c,b,a]).
序言部分的答案:

false

为什么??Prolog应该找到X=c的解决方案。bug在哪里?

在prolog中运行
跟踪可以非常有助于确定此类问题的答案。为了便于说明,我们将在此处手动进行跟踪

让我们看看您的谓词:

once(X, [H | T]) :-
    \+ X = H,
    once(X, T).
once(X, [X | T]) :-
    \+ member(X, T).

现在让我们考虑一下查询:

once(X, [b,a,a,c,b,a]).
首先,Prolog尝试谓词的第一个子句。头部是
一次(X[H|T])
,第一个表达式是
\+X=H
,它将变成:

once(X, [b|[a,a,c,b,a]]) :-  % [H|T] instantiated with [b,a,a,c,b,a] here
                             %   So, H is b, and T is [a,a,c,b,a]
    \+ X = b,
    ...
X
在这里用atom
b
实例化(与统一),统一的结果成功。但是,前面有一个否定,因此当
X
最初未绑定时,
\+X=b
的结果将为false,因为
X=b
X
b
统一,并且为true

因此,第一条没有通过。序言移到下一个子句。条款标题为
一次(X,[X | T])
,以下为
\+成员(X,T)
,成为:

once(b, [b|[a,a,c,b,a]]) :-    % X was instantiated with 'b' here,
                               %   and T instantiated with [a,a,c,b,a]
    \+ member(b, [a,a,c,b,a]).
成员(b[a,a,c,b,a])
成功,因为
b
[a,a,c,b,a]
的成员。因此,
\+成员(b[a,a,c,b,a])
失败

第二条也失败了

谓词
一次(X[b,a,a,c,b,a])
没有更多的子句。他们都失败了。因此,查询失败。主要问题是
\+X=H
(甚至
X\=H
,当
X
未实例化时,不会从列表中选择与
H
中实例化的值不同的值。其行为在逻辑上不是您想要的

对谓词更直接的方法是:

once(X, L) :-           % X occurs once in L if...
    select(X, L, R),    % I can remove X from L giving R, and
    \+ member(X, R).    % X is not a member of R
select
将根据需要查询未实例化的
X
,因此这将产生:

?- once(c, [b,a,a,c,b,a]).
true ;
false.

?-  once(b, [b,a,a,c,b,a]).
false.

?- once(X, [b,a,a,c,b,a]).
X = c ;
false.
顺便说一句,我要避免使用谓词名
,因为它是Prolog中内置谓词的名称。但它与这个特定问题无关。

使用我们可以保留

以下代码基于@Lougler的代码,但逻辑上是纯的:

onceMember_of(X,Xs) :-
   select(X,Xs,Xs0),
   maplist(dif(X),Xs0).
让我们看一些查询:

?- onceMember_of(c,[b,a,a,c,b,a]). true ; % succeeds, but leaves choicepoint false. ?- onceMember_of(b,[b,a,a,c,b,a]). false. ?- onceMember_of(X,[b,a,a,c,b,a]). X = c ; false. 让我们看一看所有不断增大的列表:

?- length(Xs,_), onceMember_of(X,Xs).
Xs = [X] ;
Xs = [X,_A],        dif(X,_A) ;
Xs = [_A,X],        dif(X,_A) ;
Xs = [ X,_A,_B],    dif(X,_A), dif(X,_B) ;
Xs = [_A, X,_B],    dif(X,_A), dif(X,_B) ;
Xs = [_A,_B, X],    dif(X,_A), dif(X,_B) ;
Xs = [ X,_A,_B,_C], dif(X,_A), dif(X,_B), dif(X,_C) ...
最后,让我们进行最一般的查询:

编辑2015-05-13 我们可以使用替换
select/3
,做得更好:

onceMember_ofB(X,Xs) :-
   selectfirst(X,Xs,Xs0),
   maplist(dif(X),Xs0).
让我们迎头运行/2的
onceMember\u
onceMember\u of b/2

?- onceMember_of(c,[b,a,a,c,b,a]).
true ;                              % succeeds, but leaves choicepoint
false.
?- onceMember_ofB(c,[b,a,a,c,b,a]).
true.                               % succeeds deterministically
但我们仍然可以做得更好!考虑一下:

?- onceMember_ofB(X,[A,B,C]). X = A, dif(A,C), dif(A,B) ; X = B, dif(B,C), dif(A,B),dif(B,A) ; % 1 redundant constraint X = C, dif(A,C),dif(C,A), dif(B,C),dif(C,B) ; % 2 redundant constraints false. 让我们看看它是否有效!

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

它们是相互排斥的案例。每个答案描述了一组与其他答案不相交的解决方案。我的目标之一是让我的序言达到我真正理解repeat答案的水平。我必须显著提高我的游戏水平。@bph。我们有共同的目标;)有趣的是,我第一次尝试编写这个过程也是一样的,我也想知道为什么我不能用第一个arg代替logvari,我认为这个错误是因为没有完全理解统一的概念,也就是说,在这里使用统一的方式是错误的,就像在程序语言“list_unique”中使用==一样,或者仅仅“unique”可能是更好的谓词名称?@bph可能,因为在一些prolog实现(如SWI)中已经使用了
once
。我只是想看看OP的名字。 ?- onceMember_ofB(X,[A,B,C]). X = A, dif(A,C), dif(A,B) ; X = B, dif(B,C), dif(A,B),dif(B,A) ; % 1 redundant constraint X = C, dif(A,C),dif(C,A), dif(B,C),dif(C,B) ; % 2 redundant constraints false.
onceMember_ofC(E,[X|Xs]) :-
   if_(E = X, maplist(dif(X),Xs),
              onceMember_ofC(E,Xs)).
?- onceMember_ofC(X,[A,B,C]).
X = A, dif(A,C), dif(A,B) ;
X = B, dif(B,C), dif(B,A) ;
X = C, dif(C,B), dif(C,A) ;
false.