據我所知,尾遞歸函數在最後一步調用自己(如return語句),但是,函數的第一個實例不會終止,直到所有其他實例都終止,因此在達到多個實例後我們會發生堆棧溢出。鑑於遞歸是最後一步,有什麼辦法在下一個實例期間或之前終止前一個實例嗎?如果一個實例的唯一目的是調用下一個實例,那麼沒有理由將它存放在內存中,對吧?尾遞歸堆棧溢出
Q
尾遞歸堆棧溢出
2
A
回答
2
是的,一些編譯器會優化尾遞歸,因此它不需要額外的堆棧空間。例如,讓我們看一下這個示例的C函數:
int braindeadrecursion(int n)
{
if (n == 0)
return 1;
return braindeadrecursion(n - 1);
}
這個函數很簡單;它只是返回1.如果沒有優化,鐺產生我的機器上的代碼看起來像這樣:
_braindeadrecursion:
00 pushq %rbp
01 movq %rsp,%rbp
04 subq $0x10,%rsp
08 movl %edi,0xf8(%rbp)
0b movl 0xf8(%rbp),%eax
0e cmpl $_braindeadrecursion,%eax
11 jne 0x0000001c
13 movl $0x00000001,0xfc(%rbp)
1a jmp 0x0000002e
1c movl 0xf8(%rbp),%eax
1f subl $0x01,%eax
22 movl %eax,%edi
24 callq _braindeadrecursion
29 movl %eax,%ecx
2b movl %ecx,0xfc(%rbp)
2e movl 0xfc(%rbp),%eax
31 addq $0x10,%rsp
35 popq %rbp
36 ret
正如你所看到的,遞歸調用是在那裏0X24。現在,讓我們嘗試更高的優化:
_braindeadrecursion:
00 pushq %rbp
01 movq %rsp,%rbp
04 movl $0x00000001,%eax
09 popq %rbp
0a ret
現在,看看那個 - 沒有更多的遞歸!這個例子非常簡單,但優化仍然可以發生在更復雜的尾遞歸情況下。
1
可以是: - 可以增加堆棧大小或 - 不要使用遞歸,而是使用某種循環。
相關問題
- 1. 堆棧溢出與尾遞歸
- 2. 試圖防止堆棧溢出與尾遞歸
- 3. scala中沒有尾遞歸優化時堆棧溢出?
- 4. 遞歸方法堆棧溢出錯誤
- 5. 堆棧溢出和遞歸方法
- 6. 遞歸java堆棧溢出錯誤
- 7. 使用遞歸溢出堆棧
- 8. 如何避免遞歸堆棧溢出?
- 9. 遞歸方法中的堆棧溢出
- 10. 遞歸函數堆棧溢出
- 11. ColdFusion:遞歸太深;堆棧溢出
- 12. 遞歸求和堆棧溢出
- 13. 爲什麼尾部遞歸Collatz猜想會導致Scheme中堆棧溢出?
- 14. 堆棧溢出
- 15. 歸併排序導致堆棧溢出
- 16. 無限遞歸函數 - >堆棧溢出錯誤
- 17. 將遞歸函數中的setTimeOut函數導致堆棧溢出?
- 18. 可以使用「遞歸」回調堆棧溢出嗎?
- 19. Haskell遞歸函數沒有堆棧溢出
- 20. 如何避免深層遞歸中的堆棧溢出
- 21. 從這個遞歸方法獲取堆棧溢出?
- 22. 導致堆棧溢出的遞歸函數
- 23. Lisp中遞歸函數調用的堆棧溢出
- 24. 在IE遞歸堆棧溢出 - > window.frames不等於自己
- 25. 函數與遞歸導致堆棧溢出
- 26. Java堆棧溢出,因爲遞歸方法調用
- 27. 遞歸調用導致堆棧溢出異常
- 28. 這個遞歸程序堆棧溢出錯誤? - C++
- 29. Java遞歸數獨求解器中的堆棧溢出錯誤
- 30. 在遞歸C++函數中捕獲「堆棧溢出」異常
我不確定這是否回答了這個問題,但是當我們處理它時,還有其他選擇:切換到優化尾部調用的實現(或僅使用需要TCO的語言);使用[trampolining](http://en.wikipedia.org/wiki/Trampoline_%28computers%29#High_Level_Programming);切換到堆上動態調整大小的堆棧,而不是使用硬件堆棧(也許這就是你的意思是「不使用遞歸」,但我認爲它是「切換到等價的迭代算法」)。 – delnan