階乘函式:
n!=n*(n-1)*(n-2)...3*2*1
針對這樣的表述,直譯成乙個過程:
(define (factorial n)
(if (=n 1)
1(* n (factorial (- n 1)))))
如果是factorial(6),其計算行為是:
(factorial 6)
(* 6 (factorial 5))
(* 6 (* 5 (factorial 4)))
(* 6 (* 5 (* 4 (factorial 3))))
(* 6 (* 5 (* 4 (* 3 (factorial 2)))))
(* 6 (* 5 (* 4 (* 3 (* 2 (factorial 1))))))
(* 6 (* 5 (* 4 (* 3 (* 2 1)))))
(* 6 (* 5 (* 4 (* 3 2))))
(* 6 (* 5 (* 4 6)))
(* 6 (* 5 24))
(* 6 120)
720現在我們換種不同的角度來計算階乘,我們可以將計算階乘n!的規則描述為:先乘以1和2,而後將得到的結果乘以3,而後再乘以4,這樣下去直到達到n.更形式的說,我們要維護乙個變動的乘積product,以及乙個從1到n的計數器counter,這一計算過程可以描述為counter和product的如下變化,從一步到下一步,它們都按照下面的規則變化:
product <- counter*product
counter <- counter-1
可以看到,n!也就是計算器counter超過n時乘積product的值。
這樣,我們可以這樣描述階乘的過程:
(define (factorial n)
(fact-iter 1 1 n))
(define (fact-iter product counter max-count)
(if (> counter max-count)
product
(fact-iter (* counter product)
(+ counter 1)
max-count)))
這樣,新的模型的6!的計算過程:
(factorial 6)
(factorial 1 1 6)
(factorial 1 2 6)
(factorial 2 3 6)
(factorial 6 4 6)
(factorial 24 5 6)
(factorial 120 6 6)
(factorial 720 7 6)
720
考慮前乙個計算過程,代換模型揭示出一種先逐步展開而後收縮的形狀,在展開的過程裡,這一計算過程構造起乙個推遲進行的操作所形成的鏈條,收縮階段表現為這些運算的實際執行。這種型別的計算過程由乙個推遲執行的運算鏈條刻畫,稱為乙個遞迴計算過程。要執行這樣的計算過程,直譯器就需要維護好那些以後將要執行的操作的軌跡。在計算階乘n!時,推遲執行的乘法鏈條的長度也就是為了儲存其軌跡需要儲存的資訊量,這個長度隨著n值而線性增長,就像計算中的步驟數目一樣。這樣的計算過程稱為乙個線性遞迴過程。
與之相對應,第二個計算過程裡並沒有任何增長或者收縮。對於任何乙個n,在計算過程中的每一步,在我們需要儲存軌跡裡,所有的東西就是變數counter, product和max-count的當前值。我們稱這種過程為乙個迭代計算過程。一般來說,迭代計算過程就是那種其狀態可以用固定數目的狀態變數描述的計算過程;而與此同時,又存在著一套固定的規則,描述了計算過程在從乙個狀態到下一狀態轉換時,這些變數的更新方式,還有乙個結束檢測,它描述這一計算過程應該終止的條件。在計算n!時,所需的計算步驟隨著n線性增長,這種過程稱為線性迭代過程。
我們還可以從另乙個角度來看這兩個過程之間的對比。在迭代的情況裡,在計算過程中的任何一點,那幾個程式變數都提供了有關計算狀態的乙個完整描述。如果我們令上述計算在某兩步之間停下來,要想重新喚醒這一計算,只需為直譯器提供有關這三個變數的值。而對於遞迴計算過程而言,這裡還存在著另外的一些隱含資訊,它們並未儲存在程式變數裡,而是由直譯器維持著,指明了在所推遲的運算所形成的鏈條裡的漫遊中,「這一計算過程處在何處」。這個鏈條越長,需要儲存的資訊也就越多。
在做迭代與遞迴之間的比較時,我們必須小心,不要搞混了遞迴計算過程的概念和遞迴過程的概念。當我們說乙個過程是遞迴的時候,論述的是乙個語法形式上的事實,說明這個過程的定義中(直接或間接地)引用了該過程本身。在說某一計算過程具有某種模式時(如線性遞迴),我們說的是這一計算過程的進展方式,而不是相應過程書寫上的語法形式。當我們說某個遞迴過程將產生出乙個迭代的計算過程時,別奇怪,上面的第二種方式就是,其事先稱為尾遞迴。(某些語言某些編譯器可能會直接將其優化為迴圈之類的動作)
遞迴 尾遞迴
計算n的階乘 function add n return n function add n 1 斐波那契數列 1 1 2 3 5 8 13.function arr n else 如上例項,但是遞迴有乙個很大的問題,就是在不斷地呼叫自身函式時,每一次呼叫都會存新的資料變數等,如果有成百上千次呼叫,則...
遞迴與尾遞迴
1 遞迴 關於遞迴的概念,我們都不陌生。簡單的來說遞迴就是乙個函式直接或間接地呼叫自身,是為直接或間接遞迴。一般來說,遞迴需要有邊界條件 遞迴前進段和遞迴返回段。當邊界條件不滿足時,遞迴前進 當邊界條件滿足時,遞迴返回。用遞迴需要注意以下兩點 1 遞迴就是在過程或函式裡呼叫自身。2 在使用遞迴策略時...
遞迴與尾遞迴
1 遞迴 簡單的來說遞迴就是乙個函式直接或間接地呼叫自身,是為直接或間接遞迴。一般來說,遞迴需要有邊界條件 遞迴前進段和遞迴返回段。當邊界條件不滿足時,遞迴前進 當邊界條件滿足時,遞迴返回。用遞迴需要注意以下兩點 1 遞迴就是在過程或函式裡呼叫自身。2 在使用遞迴策略時,必須有乙個明確的遞迴結束條件...