(SWI)序言:子目标的顺序
在Prolog中,我有两个略有不同的predicate,unique_element/2的实现。当给定元素X和列表L时谓词成功,元素X在列表中只出现一次。下面是实现和结果:(SWI)序言:子目标的顺序
实现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:我知道,可能不同和更好的实现。这里我的问题是关于改变次级目标的顺序。
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:我认为答案与您的问题有关。
希望有所帮助。
H\==Elem
正在测试目标执行时的句法不等式。但后来统一可能会使变量相同:
?- H\==Elem, H = Elem.
H = Elem.
?- H\==Elem, H = Elem, H\==Elem.
false.
所以在这里我们测试,如果他们(语法)的不同,然后将它们仍然从而统一不再是不同的。这只是一个临时测试。
另一方面,如果Elem
实际上是T
的元素,则目标member(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
(any)列表的一个独特元素[E1,E2,E3]
?
答案是三倍。考虑到一个又一个元素:
X
是E1
但只有当它是不同的,以E2
和E3
等
你能不能定义unique_element像TCOUNT Prolog - count repetitions in list
unique_element(X, List):- tcount(=(X),List,1).
也许会添加一些有趣的用途? – false
这里是另一个可能性不使用if_/3/2定义unique_element和MAPLIST/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
这两个版本都错误地失败了'unique_element(X,[a,b,c]),X = c.' – false