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

时间:2014-03-30 16:28:38

标签: list prolog predicate

我想写一个谓词,检查Element是否在List中只出现一次。

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]).

Prolog回答:

false

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

2 个答案:

答案 0 :(得分:2)

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

让我们来看看你的谓词:

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尝试你的谓词的第一个子句。头部是once(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在这里与原子b实例化(统一),并且该统一的结果成功。但是,您在此前面有否定,因此\+ X = b最初未绑定时X的结果将为false,因为X = bX与{{1}统一起来并且是真的。

第一个条款失败了。 Prolog转到下一个条款。子句头为b,后面是once(X, [X|T]),后者变为:

\+ member(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]). 成功,因为member(b, [a,a,c,b,a])b的成员。因此,[a,a,c,b,a]失败。

第二个条款也失败了。

谓词\+ member(b, [a,a,c,b,a])没有更多的子句。所有这些都失败了。所以查询失败了。主要问题是,once(X, [b,a,a,c,b,a])(甚至\+ X = H在未实例化X \= H时,不会从列表中选择与{X中实例化的值不同的值1}}。它的行为在逻辑上并不是你想要的。

更直接的谓词方法是:

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中内置谓词的名称。但它与这个特殊问题无关。

答案 1 :(得分:2)

使用我们可以保留

以下代码基于@lurker的previous answer,但逻辑纯

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.

代码单调,因此我们也获得了更为一般用途的逻辑上合理的答案!

?- onceMember_of(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.

让我们看看增加大小的所有列表:

?- 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) ...

最后,我们来看看最常见的查询

?- onceMember_of(X,Xs).
Xs = [X] ;
Xs = [X,_A],       dif(X,_A) ;
Xs = [X,_A,_B],    dif(X,_A), dif(X,_B) ;
Xs = [X,_A,_B,_C], dif(X,_A), dif(X,_B),dif(X,_C) ...

编辑2015-05-13

我们可以使用selectfirst/3替代select/3来做得更好:

onceMember_ofB(X,Xs) :-
   selectfirst(X,Xs,Xs0),
   maplist(dif(X),Xs0).

让我们onceMember_of/2onceMember_ofB/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.

请注意多余的dif/2限制?他们来自目标 maplist(dif(X),Xs0)我们可以消除它们,如下:

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.