(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:我知道,可能不同和更好的实现。这里我的问题是关于改变次级目标的顺序。

+2

这两个版本都错误地失败了'unique_element(X,[a,b,c]),X = c.' – false

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]

答案是三倍。考虑到一个又一个元素:

XE1但只有当它是不同的,以E2E3

你能不能定义unique_element像TCOUNT Prolog - count repetitions in list

unique_element(X, List):- tcount(=(X),List,1).

+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