在過程式,物件導向編程中我們使用遞迴解決問題的機會不多.但是使用遞迴方式解決問題是一種比較直觀而且簡潔的方式,不過編譯器對遞迴沒有特別的最佳化.所以我們很容易寫出效率不高的遞迴程式.而所謂尾遞迴就是在遞迴的時候進行計算.下面我以Fibonacci數列為例來說明普通遞迴和尾遞迴的不同.
普通遞迴:public long Fib_Common(int n)
{
if (n == 1 || n == 2)
return 1;
else
return Fib_Common(n - 1) + Fib_Common(n - 2);
}
這個實現簡單明了就是執行速度太慢了,因為編譯器是以如下方式進行計算的(例如計算Fib(6)):Fib(6) = Fib(5) + Fib(4); = Fib(4) + Fib(3) + Fib(3) + Fib(2); = Fib(3) + Fib(2) + Fib(2) + Fib(1) + Fib(2) + Fib(1) + Fib(2); = Fib(2) + Fib(1) + Fib(2) + Fib(2) + Fib(1) + Fib(2) + Fib(1) + Fib(2); = 8從上面的遞迴展開式可以看出Fib(4),Fib(3)都被計算了2次,而且遞迴函式以2的指數增長。所以當計算到30時就變得非常慢。
尾遞迴:public long Fib_Tail(int n)
{
if (n == 1 || n == 2)
return 1;
else
return Tail(n, 1, 1, 3);
}private long Tail(int n, long b1, long b2, int begin)
{
if (n == begin)
{
return b1 + b2;
}
else
return Tail(n, b2, b1 + b2, begin + 1);
}再來看看Fib_Tail(3)的計算過程Fib_tail(6) = Tail(6, 1, 2, 3) = Tail(6, 2, 3, 4) = Tail(6, 3, 5, 5) = 8這樣計算過程都是在每次進入遞迴函式時計算的(尾部),所以是一個線性增長。只要編譯器允許我們可以計算Fib_tail(100)都非常迅速.
正常迴圈:public long Fib_Loop(int n)
{
long b1 = 1, b2 = 1, temp, begin = 3;
for (; begin <= n; ++begin)
{
temp = b1;
b1 = b2;
b2 = temp + b2;
}
return b2;
}尾遞迴一般都是在函數式編程中出現,而且FP語言的編譯器有最佳化,實際上還是盡量轉化成迴圈形式.不過用遞迴的方式挺鍛煉思維的.