递归 尾递归
階乘函數(shù):
n!=n*(n-1)*(n-2)...3*2*1
針對(duì)這樣的表述,直譯成一個(gè)過(guò)程:
(define (factorial n)
??? (if (=n 1)
??????? 1
??????? (* n (factorial (- n 1)))))
如果是factorial(6),其計(jì)算行為是:
(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
現(xiàn)在我們換種不同的角度來(lái)計(jì)算階乘,我們可以將計(jì)算階乘n!的規(guī)則描述為:先乘以1和2,而后將得到的結(jié)果乘以3,而后再乘以4,這樣下去直到達(dá)到n.更形式的說(shuō),我們要維護(hù)一個(gè)變動(dòng)的乘積product,以及一個(gè)從1到n的計(jì)數(shù)器counter,這一計(jì)算過(guò)程可以描述為counter和product的如下變化,從一步到下一步,它們都按照下面的規(guī)則變化:
product <- counter*product
counter <- counter-1
可以看到,n!也就是計(jì)算器counter超過(guò)n時(shí)乘積product的值。
這樣,我們可以這樣描述階乘的過(guò)程:
(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!的計(jì)算過(guò)程:
(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
考慮前一個(gè)計(jì)算過(guò)程,代換模型揭示出一種先逐步展開而后收縮的形狀,在展開的過(guò)程里,這一計(jì)算過(guò)程構(gòu)造起一個(gè)推遲進(jìn)行的操作所形成的鏈條,收縮階段表現(xiàn)為這些運(yùn)算的實(shí)際執(zhí)行。這種類型的計(jì)算過(guò)程由一個(gè)推遲執(zhí)行的運(yùn)算鏈條刻畫,稱為一個(gè)遞歸計(jì)算過(guò)程。要執(zhí)行這樣的計(jì)算過(guò)程,解釋器就需要維護(hù)好那些以后將要執(zhí)行的操作的軌跡。在計(jì)算階乘n!時(shí),推遲執(zhí)行的乘法鏈條的長(zhǎng)度也就是為了保存其軌跡需要保存的信息量,這個(gè)長(zhǎng)度隨著n值而線性增長(zhǎng),就像計(jì)算中的步驟數(shù)目一樣。這樣的計(jì)算過(guò)程稱為一個(gè)線性遞歸過(guò)程。
與之相對(duì)應(yīng),第二個(gè)計(jì)算過(guò)程里并沒(méi)有任何增長(zhǎng)或者收縮。對(duì)于任何一個(gè)n,在計(jì)算過(guò)程中的每一步,在我們需要保存軌跡里,所有的東西就是變量counter, product和max-count的當(dāng)前值。我們稱這種過(guò)程為一個(gè)迭代計(jì)算過(guò)程。一般來(lái)說(shuō),迭代計(jì)算過(guò)程就是那種其狀態(tài)可以用固定數(shù)目的狀態(tài)變量描述的計(jì)算過(guò)程;而與此同時(shí),又存在著一套固定的規(guī)則,描述了計(jì)算過(guò)程在從一個(gè)狀態(tài)到下一狀態(tài)轉(zhuǎn)換時(shí),這些變量的更新方式,還有一個(gè)結(jié)束檢測(cè),它描述這一計(jì)算過(guò)程應(yīng)該終止的條件。在計(jì)算n!時(shí),所需的計(jì)算步驟隨著n線性增長(zhǎng),這種過(guò)程稱為線性迭代過(guò)程。
我們還可以從另一個(gè)角度來(lái)看這兩個(gè)過(guò)程之間的對(duì)比。在迭代的情況里,在計(jì)算過(guò)程中的任何一點(diǎn),那幾個(gè)程序變量都提供了有關(guān)計(jì)算狀態(tài)的一個(gè)完整描述。如果我們令上述計(jì)算在某兩步之間停下來(lái),要想重新喚醒這一計(jì)算,只需為解釋器提供有關(guān)這三個(gè)變量的值。而對(duì)于遞歸計(jì)算過(guò)程而言,這里還存在著另外的一些隱含信息,它們并未保存在程序變量里,而是由解釋器維持著,指明了在所推遲的運(yùn)算所形成的鏈條里的漫游中,“這一計(jì)算過(guò)程處在何處”。這個(gè)鏈條越長(zhǎng),需要保存的信息也就越多。
在做迭代與遞歸之間的比較時(shí),我們必須小心,不要搞混了遞歸計(jì)算過(guò)程的概念和遞歸過(guò)程的概念。當(dāng)我們說(shuō)一個(gè)過(guò)程是遞歸的時(shí)候,論述的是一個(gè)語(yǔ)法形式上的事實(shí),說(shuō)明這個(gè)過(guò)程的定義中(直接或間接地)引用了該過(guò)程本身。在說(shuō)某一計(jì)算過(guò)程具有某種模式時(shí)(如線性遞歸),我們說(shuō)的是這一計(jì)算過(guò)程的進(jìn)展方式,而不是相應(yīng)過(guò)程書寫上的語(yǔ)法形式。當(dāng)我們說(shuō)某個(gè)遞歸過(guò)程將產(chǎn)生出一個(gè)迭代的計(jì)算過(guò)程時(shí),別奇怪,上面的第二種方式就是,其事先稱為尾遞歸。(某些語(yǔ)言某些編譯器可能會(huì)直接將其優(yōu)化為循環(huán)之類的動(dòng)作)
總結(jié)
- 上一篇: Fibonacci(斐波纳契)数列各种优
- 下一篇: fibonacci的几种实现及尾递归