》,裡面介紹了一種技巧使得c#也能實現尾遞迴,不再像普通遞迴呼叫那樣受呼叫棧的限制。
(rec,i,n,a,b) => (n<3 ? 1 : (i==n ? a+b : rec.callback(i+1, n, b, a+b)))
(self,i,n,a,b) => (n<3 ? 1 : (i==n ? a+b : self(i+1, n, b, a+b)))是不是該將那個類繼承自delegate?可是c#不許直接繼承這個特殊類。
public static functailrecursive(func,t1,t2,t3,t4,tresult> func)另外還補充了普通遞迴的包裝方法:; do
callback = false;
} while (true);};}
public static funcrecursive(func,t1,t2,t3,t4,tresult> func)寫完**,自然還是要測試下的,我測試對比了三種方式的計算fib數列的效能:(1)最基本的遞迴呼叫的方式 (2)用上面的recursive<> (3)用上面的tailrecursive<>尾遞迴。
前兩種都屬於普通遞迴,受到呼叫棧的限制fib(10000)還可以執行,但是fib(20000)就堆疊溢位了。第三種方法由於是尾遞迴,呼叫fib(20000)自然沒問題,就是fib(10000000)也是瞬間執行完。
測試總要有資料支援才行,剛剛的測試發現,前兩種方法fib(10000)時stopwatch記錄的毫秒數都是0,無法根據花費時間來比較三者的效能。
// 外部初始化料想這**該不會被編譯器或jit優化掉吧?果然,新增後時間對比就比較明顯了:int x = 10000;
var s = x.tostring();
// 遞迴迴圈中
x = -x;
for (int j = 0; j < math.abs(x); j++)
normal可以看出,在遞迴次數較多的情況下,尾遞迴確實在效能上有較大的優勢。出乎意料的是像recursive這種簡單的包裝竟然降低了不少效能,不知有哪位大哥可以看看測試**,看到底問題出在**?或許哪天我想通了,到時可以再寫篇《續2>呵呵fib(100) = -980107325 (206 ms)
fib(1000) = 1556111435 (2419 ms)
fib(10000) = 1242044891 (65435 ms)
/////////////////////////////////////
recursive
fib(100) = -980107325 (192 ms)
fib(1000) = 1556111435 (2735 ms)
fib(10000) = 1242044891 (103156 ms)
/////////////////////////////////////
tailrecursive
fib(100) = -980107325 (186 ms)
fib(1000) = 1556111435 (1889 ms)
fib(10000) = 1242044891 (18969 ms)
測試**
staticvoid
main(
string
args)
return
(n <3?
1: (i
==n ?a
+b : fib1(i +1
, n, b, a
+b)));
};action
<
int>
fib1 =n
=>
) =
", n);
var sw
=system.diagnostics.stopwatch.startnew();
var result
=fib1(
3, n, 1,
1);sw.stop();
console.writeline(
" ( ms)
", result, sw.elapsedmilliseconds);
};fib1(
100);
fib1(
1000
);fib1(
10000
);console.writeline(
"/////////////////////////////////////");
console.writeline(
"recursive");
gc.collect();
gc.waitforpendingfinalizers();
var fib2
=recursive
<
int,
int,
int,
int,
int>
((self, i, n, a, b)
=>
return
(n <3?
1: (i
==n ?a
+b : self(i +1
, n, b, a
+b)));
});action
<
int>
fib2 =n
=>
) =
", n);
var sw
=system.diagnostics.stopwatch.startnew();
var result
=fib2(
3, n, 1,
1);sw.stop();
console.writeline(
" ( ms)
", result, sw.elapsedmilliseconds);
};fib2(
100);
fib2(
1000
);fib2(
10000
);console.writeline(
"/////////////////////////////////////");
console.writeline(
"tailrecursive");
gc.collect();
gc.waitforpendingfinalizers();
var fib3
=tailrecursive
<
int,
int,
int,
int,
int>
((self, i, n, a, b)
=>
return
(n <3?
1: (i
==n ?a
+b : self(i +1
, n, b, a
+b)));
});action
<
int>
fib3 =n
=>
) =
", n);
var sw
=system.diagnostics.stopwatch.startnew();
var result
=fib3(
3, n, 1,
1);sw.stop();
console.writeline(
" ( ms)
", result, sw.elapsedmilliseconds);
};fib3(
100);
fib3(
1000
);fib3(
10000
);}
老方也玩Cacti RRDTool續
但是,當你實現一種需求時,總想著是否能實現另外一種需求,這就有點像公司老闆對員工的期望一樣,恨不得乙個人的潛能發揮到最大,當成多個人用一樣。搞過web的人都知道,能對頁面的訪問進行授權,充許一些人訪問一些頁面。現在這種情況也需要放在我們的流量監控體系中。公司的流量監控體系所運作的範圍不只是120多台...
10 2 1 用尾遞迴避免棧溢位(續 )
10.2.1 用尾遞迴避免棧溢位 續 na ve 不像是英語,不知道什麼意思。第六章中的列表處理函式並不是尾遞迴。如果我們傳遞很大的列表,就會因棧溢位而失敗。我們將用尾遞迴重寫兩個函式 map 和 filter 將改正這個問題。為了對照,在清單 10.8 中包括了原來的實現。為了避免名字衝突,已經改...
遞迴與尾遞迴 (C語言)
在電腦科學領域中,遞迴式通過遞迴函式來實現的。程式呼叫自身的程式設計技巧稱為遞迴 recursion 乙個過程或函式在其定義或說明中有直接或間接呼叫自身的一種方法,它通常把乙個大型複雜的問題層層轉化為乙個與原問題相似的規模較小的問題來求解,遞迴策略只需少量的程式就可描述出解題過程所需要的多次重複計算...