javascript遞歸

遞歸函數就是會直接或者間接調用自身的一種函數。遞歸是一種強大的編程技術,它把一個問題分解爲一組相似的子問題,調用自身去解決它的子問題。
一、漢諾塔

問題描述:有3根柱子和一套直徑各不相同的空心圓盤。開始時源柱子上的所有圓盤都按照從小到大的順序堆疊。目標是通過每次移動一個圓盤到另一根柱子,最終把一堆圓盤移動到目標柱子上,過程中不允許把較大的圓盤放置在較小的圓盤上。
var hanoi=function(disc,src,aux,dst){
if(disc>0){
hanoi(disc-1,src,dst,aux);
console.log("move "+disc+" from "+src+" to "+dst);
hanoi(disc-1,aux,src,dst);
}
}
圓盤數量爲3的時候解法爲:
javascript遞歸

hanoi函數把一堆圓盤從一根柱子移動到另一根柱子,必要時使用輔助的柱子。
它把問題分解成3個子問題:
首先,移動一對圓盤中較小的圓盤到輔助柱子上,從而露出下面較大的圓盤。
然後,移動下面較大的圓盤到目標柱子上。
最後,它將剛纔較小的圓盤從輔助的柱子上再移動到目標柱子上。
傳遞給hanoi函數的參數包括當前移動的圓盤編號和它將用到的3根柱子。當它調用自身的時候,它去處理當前正在處理的圓盤之上的圓盤。最終,它會以一個不存在的圓盤編號去調用。此時,不執行任何操作。由於該函數對非法值不予理會,就不用擔心它會導致死循環。
二、DOM遍歷

遞歸函數可以非常高效地操作樹形結構,比如瀏覽器文檔對象模型DOM。每次遞歸調用處理指定樹的一小段。
html結構如下:
<body><div class="test">測試div</div><span class="test">測試span</span><div class="test1">test1 div</div></body>
js如下:
<script>/定義walk_the_DOM函數,它從某個指定的節點開始,按HTML源碼中的順序訪問該樹的每個節點。
它會調用一個函數,並依次傳遞每個節點給它。walk_the_DOM調用自身去處理每一個子節點。
/var walk_the_DOM=function walk(node,func){
func(node);
node=node.firstChild;
while(node){
walk(node,func);
node=node.nextSibling;
}
}
/定義getElementsByAttribute函數。它以一個屬性名稱字符串和一個可選的匹配值作爲參數。
它調用walk_the_DOM,傳遞一個用來查找節點屬性名的函數作爲參數。
匹配的節點會累加到一個結果數組中。
/var getElementsByAttribute=function(att,value){
var results=[];
walk_the_DOM(document.body,function(node){
var actual=node.nodeType===1&&node.getAttribute(att);
if(typeof actual==='string' &&( actual===value|| typeof value!=='string')){
results.push(node);
}
});
return results;
}
console.log(getElementsByAttribute("class","test"));//[div.test, span.test]
三、命名函數表達式和遞歸

1、遞歸問題
求階乘的函數:
function factorial(num){
if(num<=1){
return 1;
}else{
return num*factorial(num-1);
}
}
正常情況運行沒問題,但是下面操作會讓它出錯:
var anotherFactorial=factorial;//把函數保存在遍歷anotherFactorial中
factorial=null;//factorial置爲null,此時指向原始函數的引用只剩一個
anotherFactorial(3)
javascript遞歸

factorial已經不再是函數,所以會報錯。
2、arguments.callee實現遞歸
arguments.callee是一個指向正在執行的函數的指針,因此可以用它來實現對函數的遞歸調用
//consolefunction factorial(num){
if(num<=1){
return 1;
}else{
return num*arguments.callee(num-1);
}
}var anotherFactorial=factorial;
factorial=null;
anotherFactorial(3) //6
用arguments.callee代替函數名,可以確保無論怎樣調用函數都不會出問題。因此,在編寫遞歸函數時,使用arguments.callee總比使用函數名更保險。
問題:在嚴格模式下,不能通過腳本訪問arguments.callee,訪問這個屬性會報錯。
javascript遞歸

更多嚴格模式相關內容可參考:javascript 語句和嚴格模式(三)
3、命名函數表達式實現遞歸
創建一個名爲f()的命名函數表達式,然後賦值給factorial,即使把函數賦值給了另一個變量,函數的名字f仍然有效,所以遞歸調用照樣能正常完成。
這種方式在嚴格模式和非嚴格模式都可行。
//consolevar factorial =function f(num){'use strict'
if(num<=1){
return 1;
}else{
return num*f(num-1);
}
}
factorial(3)//6var anotherFactorial=factorial;
factorial=null;
anotherFactorial(3)//6

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章