js遞歸優化

遞歸優化

遞歸在我們平時擼碼中會經常用到,不過可能很多人不知道遞歸的弊端,就是會導致調用棧越來越深。如果沒有節制的使用遞歸可能會導致調用棧溢出。
  • 那什麼是遞歸呢?
    遞歸調用是一種特殊的嵌套調用,是某個函數調用自己或者是調用其他函數後再次調用自己的,只要函數之間互相調用能產生循環的則一定是遞歸調用,遞歸調用一種解決方案,一種是邏輯思想,將一個大工作分爲逐漸減小的小工作,比如說一個和尚要搬50塊石頭,他想,只要先搬走49塊,那剩下的一塊就能搬完了,然後考慮那49塊,只要先搬走48塊,那剩下的一塊就能搬完了,遞歸是一種思想,只不過在程序中,就是依靠函數嵌套這個特性來實現了。
  • 那什麼又是調用棧呢?
    下面的是我寫的一個簡單的遞歸調用,通過斷點我們可以看到每執行一個test函數,調用棧就會多一個test函數。
    當我們執行到i=0的時候,這個時候調用棧是最深的有11個test函數,之後又會逐個移除test函數,可以看圖二的動圖,可以看出調用棧是先進後出的
function test (i) {
    if (i < 0) return
    test(--i)
}
// 這個會調用自身11次
test(10)

圖1:
遞歸優化-調用棧
圖2:
遞歸優化-圖2

那怎麼對遞歸進行優化呢,既能起到遞歸的作用又不會加深調用棧

這裏會用到while循環的思想,調用棧之所以會加深主要是因爲方法內調用方法,必須等待方法執行完成這個任務纔算是真正的結束,就像A同學有個任務1,這個任務是讓B同學完成任務2,在B同學沒有完成之前,A同學一直處理工作狀態。
那while循環是什麼原理呢,可以理解爲將有調用關係的方法平鋪爲同一級別。這需要引入額外的方法來做調度,本來test方法需要調用自己10次的,現在用方法b通過標記的方法來決定是否需要調用test方法

  • 下面的的例子就是實現遞歸優化的實現方法(這裏複製於阮一峯的es6教程)

  • 這是一個很巧妙的方法,我說下它的實現步驟:
  1. 利用閉包將f方法保留(這裏的f方法就是我們需要遞歸調用的方法)
  2. 創建value、active、accumulated三個變量,並利用了閉包原理避免被垃圾回收
  3. accumulated是保存每次f方法調用後需要傳入f的新的形參,active是標記f方法是否執行到了最後一次循環,value是記錄需要返回的值
  4. 下面的因爲tco會return一個新的函數accumulator,所以sum=accumulator,然後再accumulator內只要accumulated長度不爲0,while就會一直執行,每次執行sum方法就會accumulated.push(arguments)方法,這樣accumulated長度就不會爲0。所以只要f.apply(this, accumulated.shift())執行的時候一旦不調用sum(x + 1, y - 1)方法,accumulated就不會有push操作,這時while就會停止。然後就是active,我們看到if (!active) {...}這個操作,這裏保證了只有第一次調用accumulator方法時會進入while循環,剩下的只是起到accumulated.push(arguments)的作用。直到while循環停止,return出來的就是經過n次調用f方法後返回的值了。

  • 這樣就可以把一個遞歸調用轉換爲while循環實現了
function tco(f) {
  var value;
  var active = false;
  var accumulated = [];

  return function accumulator() {
  // 這裏accumulated將形參入棧
    accumulated.push(arguments);
    // 這裏保證只有第一次調用纔會進入
    if (!active) {
      active = true;
      while (accumulated.length) {
        value = f.apply(this, accumulated.shift());
      }
      active = false;
      return value;
    }
  };
}

var sum = tco(function(x, y) {
  if (y > 0) {
    return sum(x + 1, y - 1)
  }
  else {
    return x
  }
});
sum(1, 100000)
這裏可能會有點難理解,可以多試幾次,打斷點看看控制檯的調用棧。相信還是可以看出其中的原理的
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章