2013-03-31 62 views
1

在這段序言代碼,我打算列出前N素數,爲什麼prolog輸出一個奇怪的樹狀列表?

(...) 

biggerPrime(N,P) :- 
    isPrime(N), 
    P is N, 
    !. 

biggerPrime(N,P) :- 
    N1 = N+1, 
    biggerPrime(N1,P). 

primeListAcc(0,A,R,R) :- !. 

primeList(N,L) :- 
    primeListAcc(N,1,[],L). 

primeListAcc(N,A,L,R) :- 
    N1 is N-1, 
    biggerPrime(A,P), 
    A1 is P+1, 
    primeListAcc(N1,A1,[P|L],R). 

如果我想倒着顯示列表排序,它工作正常:

?- primeList(5,L). 
L = [11, 7, 5, 3, 2]. 

但是,如果我改變的最後一行從代碼[P | L]到[L | P]是這樣的:

primeListAcc(N,A,L,R) :- 
     N1 is N-1, 
     biggerPrime(A,P), 
     A1 is P+1, 
     primeListAcc(N1,A1,[L|P],R). 

我得到:

?- primeList(5,L). 
L = [[[[[[]|2]|3]|5]|7]|11]. 

我錯過了什麼?這讓我很生氣!

回答

1

好極了,所以你已經發現了將元素添加到列表的結尾的問題。在Prolog中,我們可以用

add(X,L,Z):- L=[X|Z]. 

等等,什麼?如何閱讀這個?我們必須知道這裏的通話習慣。我們期待LZ進來作爲未初始化變量,我們從現在開始安排L指向一個新創建的利弊節點與X在其頭部,並Z它的尾巴。可能會在未來的某個調用中實例化爲Z

督察,我們在這裏創建的是一個開放式的名單,L = [X|Z] = [X, ...]

primeList(N,L) :- 
    primeListAcc(N,1,[],L). 

primeListAcc(N,A,Z,L) :- N > 0, % make it explicitly mutually-exclusive, 
    N1 is N-1,     % do not rely on red cuts which are easily 
    biggerPrime(A,P),    % invalidated if clauses are re-arranged! 
    A1 is P+1,      
    L = [P|R],     % make L be a new, open-ended node, holding P 
    primeListAcc(N1,A1,Z,R).  % R, the tail of L, to be instantiated further 

primeListAcc(0,A,R,R).   % keep the predicate's clauses together 

我們現在Z是不是真的在這裏需要的可以看到,因爲它承載着[]下遞歸調用鏈,不變。因此,我們可以重新寫primeListAcc沒有Z參數,因此其最終條款將

primeListAcc(0,A,R):- R=[]. 

保持Z四周,未初始化變量允許它稍後可能與一個非空列表實例,以及(中當然,只有一次(除非發生回溯))。這構成了「差異表」技術的基礎。


要回答你的字面問題 - 在這裏,可以考慮這種互動成績單:

1 ?- X=[a|b]. 

X = [a|b] 
2 ?- X=[a|b], Y=[X|c]. 

X = [a|b] 
Y = [[a|b]|c] 

[a|b]輸出是一個缺點節點被剛剛的打印方式,當它的尾巴(在這裏,b)是不是的一個列表。作爲數字,原子是而不是列表。

+0

非常有用和徹底,謝謝! – rgcalsaverini

2

回想一下,列表要麼是空列表[],要麼是函子'.'和兩個參數,其第二個參數是一個列表。語法[P|Ps]是術語'.'(P, Ps)的簡寫符號,如果Ps是一個列表(如您的示例中的情況),則這是一個列表。另一方面,術語'.'(Ps, P)(也可以寫爲[Ps|P](與您一樣),是而不是列表如果P不是列表。您可以通過reverse/2獲取反向列表。