在很多的高級語言中,都會提到尾遞歸的特性。
當年在大學裏學遞歸時,老師特別強調遞歸次數與進程棧的關係。
遞歸越多,函數入棧越多,由於進程有棧空間有線,會生成棧越界。
下圖是一個入棧的過程。
main call funcA ;
funcA call funcB
當然了,遞歸引發棧越界只是調用棧越界的方式之一,如果代碼寫的調用層級特別多,則也會引發棧越界。
隨着語言的發展(我想其中是語言的編譯器與解釋器的發展),發明一種叫“尾調用”的技術。
來個例子:
function B
{
do someting;
return 0;
}
function A
{
do someting;
return B;
}
這種A使用return B.把自己的返回與調用B聯合在了一起。這就叫尾調用。
解釋器就發現,當調用B完成之後,A的棧上的數據不會再被使用。那不如先把A從棧上POP出去。再調用B。
如下來個不是尾調用的例子。
function A1
{
do someting;
ret = B();
return ret;
}
A1在調用了B後,還會使用棧上的內容。或是如下例子也不是尾調用。
function A2
{
do someting;
return B()+1;
}
尾調用不一定出現在函數尾部,只要是最後一步操作即可。如function A3
{
if(xxx == true) then
return B(); //這裏就是尾調用。
else
a++;
end
}
尾調用的棧是怎麼玩的呢?
尾遞歸正是使用了尾調用的特性。
在寫遞歸函數時,特意使用尾調用的寫法。這樣遞歸時,棧空間不會隨着遞歸次數而增長。這樣就節約了很多的內存。
PS:我寫的文章就很一般了,在網上有很多對尾調用和尾遞歸的介紹。大家可以多參考一下。
http://www.ruanyifeng.com/blog/2015/04/tail-call.html
C語言的編譯器在加上-O2也可以識別並優化尾調用 。
有一例子,請大家自己行參考吧
http://blog.sina.com.cn/s/blog_5374d6e30100t0do.html