尾調(diào)用之所以與其他調(diào)用不同,就在于它的特殊的調(diào)用位置。
我們知道,函數(shù)調(diào)用會(huì)在內(nèi)存形成一個(gè)"調(diào)用記錄",又稱"調(diào)用幀"(call frame),保存調(diào)用位置和內(nèi)部變量等信息。如果在函數(shù)A的內(nèi)部調(diào)用函數(shù)B,那么在A的調(diào)用記錄上方,還會(huì)形成一個(gè)B的調(diào)用記錄。等到B運(yùn)行結(jié)束,將結(jié)果返回到A,B的調(diào)用記錄才會(huì)消失。如果函數(shù)B內(nèi)部還調(diào)用函數(shù)C,那就還有一個(gè)C的調(diào)用記錄棧,以此類推。所有的調(diào)用記錄,就形成一個(gè)"調(diào)用棧"(call stack)。
尾調(diào)用由于是函數(shù)的最后一步操作,所以不需要保留外層函數(shù)的調(diào)用記錄,因?yàn)檎{(diào)用位置、內(nèi)部變量等信息都不會(huì)再用到了,只要直接用內(nèi)層函數(shù)的調(diào)用記錄,取代外層函數(shù)的調(diào)用記錄就可以了。
代碼1 未進(jìn)行尾調(diào)用優(yōu)化
function factorial(n) {
if (n === 1) return 1;
return n * factorial(n - 1);
}
factorial(5) // 120
代碼2 進(jìn)行了尾調(diào)用優(yōu)化
function factorial(n, total) {
if (n === 1) return total;
return factorial(n - 1, n * total);
}
factorial(5, 1) // 120
真心是沒看出來哪里進(jìn)行了優(yōu)化,一個(gè)參數(shù)的傳遞哪里體現(xiàn)出了tco,在我看來factorial函數(shù)還是在不斷地調(diào)用自身,形成一個(gè)循環(huán)調(diào)用幀(因?yàn)槊看巫哉{(diào)用都會(huì)用到上一次外層函數(shù)的參數(shù),所以會(huì)形成一個(gè)很龐大的調(diào)用幀紀(jì)錄),但是這個(gè)例子中實(shí)在是沒看出來哪里進(jìn)行了優(yōu)化
代碼1
//使用遞歸將求和過程復(fù)雜化
function sum(x, y) {
if (y > 0) {
return sum(x + 1, y - 1);
} else {
return x;
}
}
sum(1, 10); // => 11
代碼2
function sum(x, y) {
function recur(a, b) {
if (b > 0) {
return recur(a + 1, b - 1);
} else {
return a;
}
}
//尾遞歸即在程序尾部調(diào)用自身,注意這里沒有其他的運(yùn)算
return recur(x, y);
}
sum(1, 10); // => 11
代碼2 在recur函數(shù)的外層包裹了一個(gè)函數(shù),實(shí)現(xiàn)了大框架上面recur這個(gè)尾調(diào)用不依賴sum這個(gè)外層函數(shù)的參數(shù),所以實(shí)現(xiàn)了recur是sum的尾調(diào)用(recur不依賴sum,不會(huì)存儲(chǔ)sum這個(gè)函數(shù)的調(diào)用紀(jì)錄),但是sum的內(nèi)層函數(shù)recur不還是和代碼1 一樣么,還是在繼續(xù)遞歸,又形成了很龐大的調(diào)用幀紀(jì)錄,所以不明白這樣給遞歸函數(shù)包裹了一層外層函數(shù)有什么意義?或者說這種寫法原理是什么??jī)?nèi)層函數(shù)還是該遞歸遞歸
按我的理解解決遞歸的性能就該類似動(dòng)態(tài)規(guī)劃一般:把每次的執(zhí)行結(jié)果保存在一個(gè)變量中,而每次循環(huán)只是將結(jié)果重新賦值給這個(gè)變量繼續(xù)提供給下一次循環(huán),這樣不就可以避免存儲(chǔ)上一次的調(diào)用幀,防止內(nèi)存溢出
例入:只是一個(gè)優(yōu)化方面,不用仔細(xì)帶入上面兩個(gè)疑問
代碼1
function fib(n){
if(n < 2){
return n;
}else{
return fib(n - 1) + fib(n - 2);
}
}
console.log(fib(10)); // 55
代碼2
function fibDyn(n){
var prev = 1;
var middle = 1;
var result = 1;
for(var i = 2; i < n; i++){
result = prev + middle;
prev = middle;
middle = result;
}
return result;
}
fibDyn(10) // 55尾調(diào)用由于是函數(shù)的最后一步操作,所以不需要保留外層函數(shù)的調(diào)用記錄,因?yàn)檎{(diào)用位置、內(nèi)部變量等信息都不會(huì)再用到了,只要直接用內(nèi)層函數(shù)的調(diào)用記錄,取代外層函數(shù)的調(diào)用記錄就可以了。
代碼1 未進(jìn)行尾調(diào)用優(yōu)化return n * factorial(n - 1); 在內(nèi)層結(jié)束后還需要再被n乘才能返回
代碼2return factorial(n - 1, n * total); 內(nèi)層結(jié)束了就結(jié)束了
尾遞歸會(huì)在編譯時(shí)被(一些編譯器)優(yōu)化為循環(huán),所以他不會(huì)出現(xiàn)溢出。最后一個(gè)fibDyn就是尾遞歸在“人手優(yōu)化后”的形式了
北大青鳥APTECH成立于1999年。依托北京大學(xué)優(yōu)質(zhì)雄厚的教育資源和背景,秉承“教育改變生活”的發(fā)展理念,致力于培養(yǎng)中國(guó)IT技能型緊缺人才,是大數(shù)據(jù)專業(yè)的國(guó)家
達(dá)內(nèi)教育集團(tuán)成立于2002年,是一家由留學(xué)海歸創(chuàng)辦的高端職業(yè)教育培訓(xùn)機(jī)構(gòu),是中國(guó)一站式人才培養(yǎng)平臺(tái)、一站式人才輸送平臺(tái)。2014年4月3日在美國(guó)成功上市,融資1
北大課工場(chǎng)是北京大學(xué)校辦產(chǎn)業(yè)為響應(yīng)國(guó)家深化產(chǎn)教融合/校企合作的政策,積極推進(jìn)“中國(guó)制造2025”,實(shí)現(xiàn)中華民族偉大復(fù)興的升級(jí)產(chǎn)業(yè)鏈。利用北京大學(xué)優(yōu)質(zhì)教育資源及背
博為峰,中國(guó)職業(yè)人才培訓(xùn)領(lǐng)域的先行者
曾工作于聯(lián)想擔(dān)任系統(tǒng)開發(fā)工程師,曾在博彥科技股份有限公司擔(dān)任項(xiàng)目經(jīng)理從事移動(dòng)互聯(lián)網(wǎng)管理及研發(fā)工作,曾創(chuàng)辦藍(lán)懿科技有限責(zé)任公司從事總經(jīng)理職務(wù)負(fù)責(zé)iOS教學(xué)及管理工作。
浪潮集團(tuán)項(xiàng)目經(jīng)理。精通Java與.NET 技術(shù), 熟練的跨平臺(tái)面向?qū)ο箝_發(fā)經(jīng)驗(yàn),技術(shù)功底深厚。 授課風(fēng)格 授課風(fēng)格清新自然、條理清晰、主次分明、重點(diǎn)難點(diǎn)突出、引人入勝。
精通HTML5和CSS3;Javascript及主流js庫(kù),具有快速界面開發(fā)的能力,對(duì)瀏覽器兼容性、前端性能優(yōu)化等有深入理解。精通網(wǎng)頁(yè)制作和網(wǎng)頁(yè)游戲開發(fā)。
具有10 年的Java 企業(yè)應(yīng)用開發(fā)經(jīng)驗(yàn)。曾經(jīng)歷任德國(guó)Software AG 技術(shù)顧問,美國(guó)Dachieve 系統(tǒng)架構(gòu)師,美國(guó)AngelEngineers Inc. 系統(tǒng)架構(gòu)師。