Javascript的函數(shù)遞歸

遞歸是什么?

想個標(biāo)題已經(jīng)讓我很糾結(jié)了,究竟是函數(shù)遞歸還是遞歸函數(shù)?

看了下MDN的說明是:** 函數(shù)可以被遞歸,就是說函數(shù)可以調(diào)用其本身。**

那遞歸就是函數(shù)的里面再調(diào)用自己,從而形成一個循環(huán)。

還是在實戰(zhàn)才會學(xué)得更快,來戰(zhàn)吧。

用遞歸計算階乘:

function factorial(n){
    return (n * factorial(n - 1));
}

這里就是函數(shù)里,調(diào)用了自己不停的互乘。當(dāng)運行factorial(5)就不停計算 5*4*3*2...的計算下去,但問題是這沒有盡頭呀!直到棧溢出拋出 Uncaught RangeError: Maximum call stack size exceeded(…) 也不會有返回值。

這種無盡的遞歸稱為死遞歸,它和死循環(huán)一樣恐怖。

所有循環(huán)的程序都會有一個退出條件,對于每一個遞歸的程序,也都會有一個退出條件,加入退出條件后,函數(shù)就成了這樣:

function factorial(n){
  if ((n == 0) || (n == 1))
    return 1;
  else
    return (n * factorial(n - 1));
}
factorial(5); // 120

使用遞歸的時候要注意出口,不return出口會造成死循環(huán)。

看到這里,你可能會想說遞歸不就是循環(huán)嗎?這種for都可以簡單實現(xiàn)啦。

沒錯,遞歸就是一個循環(huán),自身的一個循環(huán)。

循環(huán)的時間復(fù)雜度和空間復(fù)雜度都優(yōu)于用遞歸實現(xiàn)。

遞歸是方法調(diào)用本身,總是要進出堆棧內(nèi)存,容易導(dǎo)致內(nèi)存溢出,并且運行慢,循環(huán)是直接在內(nèi)存中執(zhí)行,速度快。

遞歸的優(yōu)越性在于條理清晰,可讀性強,比較適宜于問題本身是遞歸性質(zhì)的、用循環(huán)難于解決的問題。在二者都不難的情況下,一般都是優(yōu)先選用循環(huán)來解決問題的。

寫一個99乘法表

這一個簡單的實戰(zhàn)例子,相信剛開始學(xué)javascript時,老師都給你布置這道作業(yè),直接用兩個for就可以輕松實現(xiàn):

可以直接復(fù)雜代碼,放到控制臺就可以運行

for(var i=1; i<=9; i++){
    for(var j=1; j<=i; j++){
        document.writeln(j+" x "+i+"="+i*j);
       if (j==i) {
           document.writeln("<br>");
       }
    }
}

效果如下:

99sfb.jpg

讓我跟據(jù)遞歸簡單的實現(xiàn)一個:

var ss = function(x){
    if(x>0 && x<=9){
        for(var i=1;i<=x;i++){
            document.writeln(i+"x"+x+"="+x*i);
            if(i==x){
                document.write("<br>");
            }
        }
        ss(++x)
    }
}
ss(1)//從1開始;改成9,函數(shù)里就要改成減了,實現(xiàn)出來就倒過來了

效果一模一樣(哈哈,其實我是用了同一張圖...):

99sfb.jpg

上面函數(shù)還是有用到for呀,下面我們用純的:

var jj = function(x,y){
    if(x==1&&y==1){
        document.writeln(x+"x"+y+"="+x*y);
        return false;//注意這個return出口,不然會死循環(huán)
    }
    if(y==1){
        document.writeln(x+"x"+y+"="+x*y);
        document.writeln("<br>");
        x--;
        y=x;
    }else{
        document.writeln(x+"x"+y+"="+x*y);
        --y;
    }
    jj(x,y);
}
jj(9,9)

這個也可以實現(xiàn)效果,只是真的是倒過來了(哈哈....)

99fsb2.jpg

** 在知乎看到有個大神,用遞歸實現(xiàn) javascript 中常用的 forEach map filter 函數(shù)。
作者:韋捷 訪問地址:https://www.zhihu.com/question/54359554/answer/139040476 **

下面讓我把實現(xiàn)方法搬運過來裝裝逼:

先編寫兩個輔助函數(shù):

// 得到 array 的第一個元素
// head([1,2,3,4,5])
// => 1
var head = function (array)  {
  return array[0]
}

// 得到 array 除第一個元素外的其他元素
// tail([1,2,3,4,5])
// => [2,3,4,5]
var tail = function (array) {
  return array.slice(1, array.length)
}

forEach 時會遍歷作為第一個參數(shù)的數(shù)組,并且逐個將數(shù)組中的成員作為參數(shù)傳給 fn 函數(shù)執(zhí)行。

var forEach = function (array, fn) {
  if (array.length > 0) {
    fn(head(array))//傳進的函數(shù)調(diào)用數(shù)組的第一個值
    forEach(tail(array), fn)//重新調(diào)用剩下的數(shù)組
  }
}
forEach([1,2,3], function (x) { console.log(x) })
// => 1
// => 2
// => 3

map 時會遍歷作為第一個參數(shù)的數(shù)組,并且逐個將數(shù)組中的成員作為參數(shù)傳給 fn 函數(shù)執(zhí)行,然后將 fn 的返回值逐個裝入新數(shù)組,最后返回新數(shù)組。

var map = function (array, fn) {
  if (array.length > 0) {
    return [fn(head(array))].concat(map(tail(array), fn))//實現(xiàn)每個值+1后合并
  } else {
    return []
  }
}
map([1,2,3], function (x) { return x+1 })
// => [2,3,4]

filter 時會遍歷作為第一個參數(shù)的數(shù)組,并且逐個將數(shù)組中的成員作為參數(shù)傳給 fn 函數(shù)執(zhí)行,如果 fn 的返回值是 true 則將當(dāng)前參數(shù)裝入新數(shù)組,否則忽略當(dāng)前參數(shù),最后返回新數(shù)組。

var filter = function (array, fn) {
  if (array.length > 0) {
     if (fn(head(array)) === true) {
       return [head(array)].concat(filter(tail(array), fn))//實現(xiàn)每個>2的值合并
     } else {
       return [].concat(filter(tail(array), fn))
     }
  } else {
     return []
  }
}
filter([1,2,3,4,5], function (x) { return x>2 })
// => [3,4,5]

JSON中最常用到

用到了遞歸函數(shù),用來調(diào)用json的子節(jié)點,把所有json中的子節(jié)點中包含某個數(shù)的object,都push到一個數(shù)組中,然后對其進行綁定。

var arr=[];
function getData(data){
    if(data.itemId=="4323"){
        arr.push(item_data);
    }
    if(data.Childs){
      if(data.Childs.length>0){
          getChilds(data.Childs)
      }
   }
}
function getChilds(data){
    for(var i=0;i<data.length;i++){
        getData(data[i]);
    }
}

更深入的應(yīng)用還有二叉樹的遍歷等...

** 參考資料 **

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容