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
在这里用atomb
实例化(与统一),统一的结果成功。但是,前面有一个否定,因此当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.