我想写一个谓词,检查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解决方案。臭虫在哪里?
答案 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 = b
将X
与{{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)
使用prolog-dif我们可以保留logical-purity!
以下代码基于@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) ...
我们可以使用selectfirst/3
替代select/3
来做得更好:
onceMember_ofB(X,Xs) :-
selectfirst(X,Xs,Xs0),
maplist(dif(X),Xs0).
让我们onceMember_of/2
和onceMember_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.