2017-04-21 52 views
5

在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

回答

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:我認爲答案與您的問題有關。

希望有所幫助。

7

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

4

這裏是另一個可能性不使用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