我在Prolog中有两个稍微不同的谓词unique_element / 2实现。当给定元素X和列表L时,谓词成功,元素X在列表中仅出现一次。以下是实施和结果:
实施1:
%%% unique_element/2
unique_element(Elem, [Elem|T]) :-
not(member(Elem, T)).
unique_element(Elem, [H|T]) :-
member(Elem, T),
H\==Elem,
unique_element(Elem, T),
!.
结果:
?- unique_element(X, [a, a, b, c, c, b]).
false.
?- unique_element(X, [a, b, c, c, b, d]).
X = a ;
X = d.
实施2:
%%% unique_element/2
unique_element(Elem, [Elem|T]) :-
not(member(Elem, T)).
unique_element(Elem, [H|T]) :-
H\==Elem,
member(Elem, T),
unique_element(Elem, T),
!.
如果你第一眼就没注意到:“H \ == Elem”和“成员(Elem,T)”在第二个impl,规则2上被翻转。
结果:
?- unique_element(X, [a, a, b, c, c, b]).
X = a.
?- unique_element(X, [a, b, c, c, b, d]).
X = a ;
X = d.
问题:在这种情况下,订单如何影响结果?我意识到规则/事实/等的顺序很重要。然而,翻转的两个特定规则似乎没有“连接”或以某种方式相互影响(例如,在错误的位置/顺序中的“切割”谓词)。
注意:我们在这里谈论SWI-Prolog。
注2:我知道,可能是不同的和更好的实现。我的问题是关于改变子目标的顺序。
答案 0 :(得分:7)
H\==Elem
正在测试目标执行时的句法不平等。但后来的统一可能会使变量相同:
?- H\==Elem, H = Elem.
H = Elem.
?- H\==Elem, H = Elem, H\==Elem.
false.
所以在这里我们测试它们是否(语法上)不同,然后它们是统一的,因此不再是不同的。因此,这只是一个临时的考验。
如果member(Elem, T)
实际上是Elem
的元素,则目标T
为真。考虑:
?- member(Elem, [X]).
Elem = X.
可以理解为
(何时)是否认为
Elem
是列表[X]
的元素?
,答案是
在某些情况下,即
Elem = X
。
如果您现在在程序中混合使用这些不同类型的目标,您会得到奇怪的结果,只能通过详细检查您的程序来解释。
作为初学者,最好只坚持Prolog的纯粹部分。在你的情况下:
使用dif/2
代替\==
不要使用剪切 - 在您的情况下,它将答案数限制为两个。如在
unique_element(X, [a,b,c])
请勿使用not/1
或(\+)/1
。它产生更多的不正确性。在unique_element(a,[a,X]),X=b.
正确成功时,请考虑X=b,unique_element(a,[a,X])
错误地失败。
这是您程序的直接纯化版本。还有改进的余地!
non_member(_X, []).
non_member(X, [E|Es]) :-
dif(X, E),
non_member(X, Es).
unique_element(Elem, [Elem|T]) :-
non_member(Elem, T).
unique_element(Elem, [H|T]) :-
dif(H,Elem),
% member(Elem, T), % makes unique_element(a,[b,a,a|Xs]) loop
unique_element(Elem, T).
?- unique_element(a,[a,X]).
dif(X, a)
; false. % superfluous
?- unique_element(X,[E1,E2,E3]).
X = E1,
dif(E1, E3),
dif(E1, E2)
; X = E2,
dif(E2, E3),
dif(E1, E2)
; X = E3,
dif(E2, E3),
dif(E1, E3)
; false.
注意最后一个查询是如何读取的?
X
何时是(任意)列表[E1,E2,E3]
的唯一元素?
答案是三重的。考虑一个元素接一个:
X
为E1
,但仅限于E2
和E3
等
答案 1 :(得分:6)
TL; DR :阅读文档并找出原因:
?- X = a, X \== a.
false.
?- X \== a, X = a.
X = a.
我想知道你为什么这么靠近自己搞清楚; - )
有很多方法可以比较Prolog中的东西。至少,你有统一,有时可以比较,有时甚至更多;比你有平等,它的否定,你正在使用的那个。那么它做了什么:
?- a \== b. % two different ground terms
true.
?- a \== a. % the same ground term
false.
现在它变得有趣了:
?- X \== a. % a free variable and a ground term
true.
?- X \== X. % the same free variable
false.
?- X \== Y. % two different free variables
true.
我建议您执行以下操作:弄清楚member/2
如何处理它(它是否使用统一?等效?还有其他什么?)然后替换上面所有示例中使用的member/2
并查看结果是否有任何不同。
既然你正在努力确保事情有所不同,那么试试dif/2
所做的事情。如:
?- dif(a, b).
或
?- dif(X, X).
或
?- dif(X, a).
等等。
另请参阅this question and answers:我认为答案与您的问题相关。
希望有所帮助。
答案 2 :(得分:5)
你能否定义像tcount Prolog - count repetitions in list
这样的unique_element unique_element(X, List):- tcount(=(X),List,1).
答案 3 :(得分:4)
另一种可能性是使用if_/3和maplist / 2来定义unique_element / 2:
:- use_module(library(apply)).
unique_element(Y,[X|Xs]) :-
if_(Y=X,maplist(dif(Y),Xs),unique_element(Y,Xs)).
与@ user27815非常优雅的解决方案(+ s(0))相比,此版本不构建在clpfd上(由tcount/3使用)。 OP提供的示例查询按预期工作:
?- unique_element(a,[a, a, b, c, c, b]).
no
?- unique_element(X,[a, b, c, c, b, d]).
X = a ? ;
X = d ? ;
no
@false提供的示例现在可以成功而不会留下多余的选择点:
?- unique_element(a,[a,X]).
dif(a,X)
另一个更通用的查询产生相同的结果:
?- unique_element(X,[E1,E2,E3]).
E1 = X,
dif(X,E3),
dif(X,E2) ? ;
E2 = X,
dif(X,E3),
dif(X,E1) ? ;
E3 = X,
dif(X,E2),
dif(X,E1) ? ;
no