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)
這里可能會有點難理解,可以多試幾次,打斷點看看控制台的調用棧。相信還是可以看出其中的原理的


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM