2016-05-09 18 views
2

我是新來的Prolog,當它涉及到遞歸算法是不是偉大的,因爲是,所以我很困惑與以下兩個條款:尺寸過程語言

size([], 0). 
size([H|T], N) :- size(T, N1), N is N1+1. 

我無法追查這問題爲:

?- size([a,b,c,d], N). 

這將與第二條統一到形式:

size([a,b,c,d], N) :- size([b,c,d], N1), N is N1+1. 

但我與N混淆是因爲N1+1這些變量永遠不會統一。這些變量的值是什麼?

任何關於這個問題,或者這個算法的一個簡單的跟蹤幫助,將不勝感激。

+0

''''評估時,'**'肯定是統一的,因爲''執行從左到右,遞歸調用綁定'N' - 最終通過統一(本地版本)用'0'表示。 –

+0

感謝您的快速回復。我不確定我是否明白你的意思。跟蹤過程中的哪個N與0統一? – JmanxC

+0

我自己解決了問題:首先統一'N1',或者使用'0'或者從大小([H | T],N)'中的低級'N'',然後使用N1來計算頂層'N'。訣竅是瞭解在遞歸算法中,*有幾個不同版本的'N',每個版本都有不同的值*。在你的例子中,這些不同的'N'將被綁定到0,1,2,3和4. –

回答

4

N爲N1 + 1,因爲這些變量從不統一。

我認爲你的意思是說他們永遠不會實例化,即他們永遠不會獲得價值。統一兩個變量可以實例化它們,但不是必需的。例如,你可以在一個序言REPL運行此:

?- N = N1. 
N = N1. 

NN1沒有任何價值(還),他們都是統一;如果N1稍後被實例化,則N將被實例化爲具有相同的值。另一個不那麼瑣碎,例如:

?- [H|T] = L, L = [1|M], writeln(H). 
1 
H = 1, 
T = M, 
L = [1|M]. 

但是,這是事實,N不會與N1+1統一! is將評估N1+1值將與N統一。後內size([b,c,d],N1)進行了評估,因此N1變量將被實例會發生這種情況。

從本質上講,執行將是這樣的:

size([a,b,c,d],N). 
      size([b,c,d],N1) 
       size([c,d],N1) 
        size([d],N1) 
         size([],0) 
         N is 0+1. 
        N is 1+1. 
       N is 2+1. 
      N is 3+1. 

這是一個有點低效的,因爲我們需要保留所有的函數調用內存;看看尾部遞歸和累加器來解決這個問題。

還要注意的是N1需要,只是因爲is是要計算表達式被實例化。你可以寫這樣的事情:

size([],0). 
size([_|T], 1+ N1):-size(T,N1). 

,如果你把它叫做:

?- size([1,2],N). 
N = 0+1+1. 

樂趣,不是嗎?您可能需要評估最後一個N,例如將其稱爲size([1,2],N), Result is N。但是,一個問題是,我們在內存中保留了一個可以變得非常快的0+1+1+1+1+......鏈。所以最好將這個技巧用於不會增長的事物,例如表達模板。

+1

實際上,後一個變體**是**尾遞歸!高效?不,但有趣的... – repeat

+1

@repeat是的,我認爲這是一個很好的方式來顯示尾遞歸版本和普通版本之間的區別,而不必解釋累加器;然後可以使用累加器的版本作爲優化算術運算的方式 –

+2

第二種想法是,您可能希望通過將size/2的第二個子句更改爲right-leaning來將表達式修改爲right-leaning '尺寸([_ | Es],1 + N): - 尺寸(Es,N)。「 – repeat