2012-02-29 88 views
10

我有一個遞歸函數來移動畫布上的一些圓圈。內圈被放大(放大),所有其他圈被推開。 推動的圓圈推動其他圓圈等,直到縮放完成。JavaScript遞歸:超出最大調用堆棧大小

我得到一個錯誤「最大調用堆棧大小超過了」,我理解這個問題,但我只是不知道如何解決它... 我發現了三個可能的解決方案用於解決一般的遞歸問題:

  1. 更改遞歸進行迭代
  2. 使用memoization
  3. 使用的setTimeout

但我認爲,我可以使用他們沒有:

  1. 我無法實現,因爲操作的未知數量需要反覆
  2. 我不明白,記憶化不夠好,但我認爲它不適合或者(或也許我錯了,有人可以告訴我)
  3. 我不能使用SetTimeout,因爲它應該阻止在這個特定動畫的函數調用。

如何解決此問題?

// Pushes circles aside when some other circle leans on these circles (on zoom in) 
var moveCirclesAside = function(circle1, circleToSkip, groupOfMoves) { 
    var count = circles.length; 
    for (var i = 0; i < count; i++) { 

     // Skip the same circle 
     if (i == circle1.i) { 
      continue; 
     } 

     // Also skip the circle which was intended not to move any further 
     if (circleToSkip != null && i == circleToSkip.i) { 
      continue; 
     } 

     // Get second circle 
     var circle2 = circles[i]; 

     // Calculate a distance between two circles 
     var dx = circle2.x - circle1.x; 
     var dy = circle2.y - circle1.y; 
     var distance = Math.sqrt((dx * dx) + (dy * dy)); 

     // If circles already collided need to do some moving... 
     if (distance <= circle1.r + circle2.r + OD.config.circleSpacing) { 

      // Get collision angles 
      var angle = Math.atan2(dy, dx); 
      var sine = Math.sin(angle); 
      var cosine = Math.cos(angle); 

      // Some circle position calculation 
      var x = OD.config.circleSpacing; 
      var xb = x + (circle1.r + circle2.r); 
      var yb = dy * cosine - dx * sine; 

      // Save each state (move) of any circle to the stack for later rollback of the movement 
      groupOfMoves.push(copyCircleByVal(circle2)); 

      // Move the circle 
      circle2.x = circle1.x + (xb * cosine - yb * sine); 
      circle2.y = circle1.y + (yb * cosine + xb * sine); 

      // Make sure that circle won't go anywhere out of the canvas 
      adjustCircleByBoundary(circle2); 

      // If moved circle leans against some other circles make sure that they are moved accordingly 
      // And such related moves must be grouped for correct rolback of moves later - so we pass 'groupOfMoves' var 
      moveCirclesAside(circle2, circle1, groupOfMoves); 
     } 
    } 
}; 

回答

5

這並不讓人感到意外,因爲算法迭代時堆棧增長,但退出條件不可預知,操作沒有嚴格定位(它們對鄰近圓圈有連鎖效應),所以處理時間會變得混亂。

我會重新考慮算法。考慮找到兩個最接近的圓,如果這些圓比分開的給定閾值更遠,則中止。否則,將它們分開並重復一次。

7

1)我無法實現,因爲所需要操作的未知數量的迭代;

嗯,我沒有看過你的代碼,但線性遞歸的一般避免(你在這裏有一個線性的)看起來是這樣的:

while (1 == 1) { 
    if (breakcondition) 
     break; 
    doSomeCode() 
} 

所以你不必知道確切的迭代次數,如for - 循環的情況。

3

您不需要知道您製作迭代解決方案所需的數量或操作。重點是用你自己的一個替換JavaScript棧。查看此答案以查看如何實現它的示例:Link

您可以在JavaScript中使用Array對象作爲堆棧,因爲它支持push()pop()。 PS:按照Jim的回答,你通常可以找到一個算法,它不需要這麼多的遞歸級別。

+2

您的回答也很有幫助,但可悲的是我只能接受一個答案......謝謝! – fizis 2012-02-29 13:35:32

+1

我已經爲你表達了這種感悟。 – agm1984 2017-11-12 21:07:11

相關問題