碰巧,你的錯誤與遞歸無關。你的問題是,你調用了一個方法兩次,而不是保存它的價值。如果你不打印結果,你甚至不會意識到這一點。作爲@JLK指出,你在這個片段中調用它兩次:通過代碼
Console.WriteLine("{0} * {1}", n, Factorial(n - 1));
return n * Factorial(n - 1);
讓我們一步,這樣我們就可以理解輸出:
Factorial (3);
-> output 3 * {Factorial (2)} // 3 * 2 (4)
-> output 2 * {Factorial (1)} // 2 * 1 (2)
-> output 1 * {Factorial (0)} // 1 * 1 (1)
<- 1
Factorial (0)
<- 2
Factorial (1)
-> output 1 * {Factorial (0)} // 1 * 1 (3)
<- 1
Factorial (0)
<- 1
<- 1
Factorial (2)
-> output 2 * {Factorial (1)} // 2 * 1 (6)
-> output 1 * {Factorial (0)} // 1 * 1 (5)
<- 1
Factorial (0)
<- 1
Factorial (1)
-> output 1 * {Factorial (0)} // 1 * 1 (7)
<- 1
Factorial (0)
<- 1
<- 2
這可能是一個有點混亂,但這對於遞歸是正常的。所以基本上所有你需要做的是改變代碼片段一點點:
var factorial = Factorial (n - 1);
Console.WriteLine("{0} * {1}", n, factorial);
return n * factorial;
輸出則是:
1 * 1
2 * 1
3 * 2
6
如果您想了解更多關於遞歸,我d建議this網站。這不是關於C#,但它是一個很好的學習遞歸的網站。如果你想在遞歸中做很多事情,我不會推薦C#,因爲它是一種命令式語言。對於遞歸函數語言更適合。如果你仍然想保持接近C#,或至少使用.Net框架,我建議使用F#,這是C#的功能等價物。
C#和遞歸的主要問題是,C#不支持Tail recursion。這可以很容易地導致StackOverflows,當你做更深的遞歸調用(例如無限遞歸循環)。這是因爲每次你調用一個函數時,你調用該函數的代碼地址被壓入堆棧。所以每次你做一個遞歸調用時,代碼地址都被推送到堆棧。所以,如果你有例如這樣的功能:
void rec() => rec();
你在更短的通過調用它獲得的StackOverflow比在C#中的第二個。尾遞歸至少部分解決了這個問題:如果遞歸調用是函數中的最後一次執行,它不會推送調用者的代碼地址。因此,在F#這個一直運行:
let rec r() =
r()
r()
(我不會去深入到F#的語法,有足夠的在線教程)。我說它只解決了部分問題,因爲這個:
let rec r() =
r()
()
r()
導致StackOverflow再一次。我不知道任何編程語言,如果情況並非如此,那麼您只需習慣使用尾遞歸。
您可以在this後閱讀更多關於功能/遞歸語言。
我投票關閉這一問題作爲題外話,因爲被調試的幫助:這個代碼是如何工作的? – danihp
@danihp基本上有人(@yosu)犯了一個錯誤,這也可能發生在其他人身上。所以這甚至可以幫助其他人,有一個類似的問題。它不是那麼廣泛,它只是調試幫助,這是一個關於遞歸的相當緊湊的問題。 – MetaColon
@MetaColon代碼有一個很大的錯誤,因爲這個輸出沒有意義。 OP要求忽略這個錯誤。這是家庭作業。 OP應該爲自己做。它們是雙遞歸,應該是簡單的。 – danihp