2011-07-12 87 views
3

我在開發Web服務器上運行下面的遞歸函數時遇到問題。它會導致堆棧溢出。在調試模式下,本地運行良好。這裏是我已經嘗試過的東西:當不在本地運行時,F#尾遞歸堆棧溢出

  1. 確保在生成選項下啓用了「生成尾呼叫」。
  2. 我跑了反彙編程序,並按照這裏的說明:http://blogs.msdn.com/b/fsharpteam/archive/2011/07/08/tail-calls-in-fsharp.aspx它不會出現要使用尾遞歸。
  3. 我試過重寫它而不使用遞歸,但我的F#技能不是最好的。

所以我的問題是:

  1. 是這個功能將能夠使用尾部遞歸?
  2. 爲什麼它會在調試模式下通過VS本地工作,而不是在開發Web服務器上?

謝謝!

let rec SimulationLoop (rowNum : int) (h : double) (time : double) (v : double) (s : double) (p : double) (newV' : double) (newS' : double) (newP' : double) (designParameters : DesignParameters) (inputs : ISimulationInputProvider) = seq { 
    //let timer = System.Diagnostics.Stopwatch.StartNew() 
    let finalTime = (6.0 * inputs.ShockAbsorber.Stroke/designParameters.VelocityAfterImpact)  
    let startH = StartH h time finalTime 

    let slopes = Slopes v s p newV' newS' newP' startH designParameters inputs 
    let vSlope, sSlope, pSlope = slopes 
    let betaList = [ for j in 0 .. 5 -> beta.[j].[4] ] 
    let newV' = CalcPrime v startH vSlope betaList 
    let newS' = CalcPrime s startH sSlope betaList 
    let newP' = CalcPrime p startH pSlope betaList 

    let delta = Delta h slopes 
    let tau = Tau v s p 

    let rowResult, rowNum, time, newV, newS, newP = if delta < tau then RecordResults rowNum time startH v s p slopes designParameters inputs else None, (rowNum + 1), time, v, s, p 
    let loop = newS < inputs.ShockAbsorber.Stroke - 0.01 && newV >= 0.0 && rowNum <= 8000 && (time < finalTime && time + h > time) 
    let stepLength = StrokeStepLength inputs.ShockAbsorber.Stroke designParameters.HoleSize 
    let endH = EndH delta startH tau stepLength newV 

    //timer.Stop() 
    //System.Diagnostics.Debug.WriteLine("Row: " + rowNum.ToString() + " = " + timer.ElapsedMilliseconds.ToString()) 
    match (rowResult, loop) with 
     | Row(r), true -> 
      yield r 
      yield! SimulationLoop rowNum endH time newV newS newP newV' newS' newP' designParameters inputs 
     | Row(r), false -> 
      yield r 
     | None, true -> 
      yield! SimulationLoop rowNum endH time newV newS newP newV' newS' newP' designParameters inputs 
     | None, false ->() 
} 
+1

由於我在序列表達式中,我相信它必須是產量。 – sst4460

+0

事實上,它不能'返回!'。 –

+0

你有沒有嘗試過使用Seq.unfold實現這個? – Ankur

回答

0

我最終沒有遞歸重寫它。這不是最優雅的解決方案,但它的工作原理:

let SimulationLoop (rowNum : int) (h : double) (time : double) (v : double) (s : double) (p : double) (newV' : double) (newS' : double) (newP' : double) (designParameters : DesignParameters) (inputs : ISimulationInputProvider) = 
    let mutable mKeepLooping = true 
    let mutable mRowNum = 1 
    let mutable mEndH = h 
    let mutable mTime = time 
    let mutable mNewV = v 
    let mutable mNewS = s 
    let mutable mNewP = p 
    let mutable mNewV' = newV' 
    let mutable mNewS' = newS' 
    let mutable mNewP' = newP' 

    let theList = new List<SimulationRow>() 

    while mKeepLooping do 
     let finalTime = (6.0 * inputs.ShockAbsorber.Stroke/designParameters.VelocityAfterImpact) 
     let startH = StartH mEndH mTime finalTime 

     let slopes = Slopes mNewV mNewS mNewP mNewV' mNewS' mNewP' startH designParameters inputs 
     let vSlope, sSlope, pSlope = slopes 
     let betaList = [ for j in 0 .. 5 -> beta.[j].[4] ] 
     let mNewV' = CalcPrime v startH vSlope betaList 
     let mNewS' = CalcPrime s startH sSlope betaList 
     let mNewP' = CalcPrime p startH pSlope betaList 

     let delta = Delta mEndH slopes 
     let tau = Tau mNewV mNewS mNewP 

     let rowResult, rowNum, time, newV, newS, newP = if delta < tau then RecordResults mRowNum mTime startH mNewV mNewS mNewP slopes designParameters inputs else None, (mRowNum + 1), mTime, mNewV, mNewS, mNewP 
     mRowNum <- rowNum 
     mTime <- time 
     mNewV <- newV 
     mNewS <- newS 
     mNewP <- newP 
     let loop = newS < inputs.ShockAbsorber.Stroke - 0.01 && newV >= 0.0 && rowNum <= 8000 && (time < finalTime && time + h > time) 
     mKeepLooping <- loop 
     let stepLength = StrokeStepLength inputs.ShockAbsorber.Stroke designParameters.HoleSize 
     mEndH <- EndH delta startH tau stepLength newV 

     match rowResult with 
     | Row(r) -> 
      theList.Add(r) 
     | _ ->() 

    theList 
1

因爲你的函數的主體是一個序列的表達時,編譯器不使用尾遞歸。但是,僅僅調用SimulationLoop肯定不會導致堆棧溢出,因爲它應該只是生成序列而不評估其內容。此外,考慮到代碼的性質,我期望編譯器生成的狀態機用於逐步執行序列,而不會溢出堆棧。

當您看到錯誤時,您如何使用調用SimulationLoop的結果?本地和網絡機器的平臺是什麼(例如它們都是32位的)?如果您修剪您的示例(例如,通過刪除對CalcPrimeRecordResults等的呼叫),您是否看到相同的行爲?

+0

兩臺機器都是64位。我曾嘗試將行數限制設置爲更低的數字,800(請參閱let循環聲明),並且在這種情況下工作。但是,我需要它高於(8000)。所以我相當確信它與SimulationLoop的調用次數有關。 – sst4460

+0

除此之外,我已經通過Visual Studio調試器運行的其他開發人員的機器上嘗試過它併成功運行。我已經在兩個開發Web服務器(都是Windows Server 2008 64位)上嘗試過它們,它們都堆棧溢出。 – sst4460

+0

外部機會:部署機器是否與開發機器具有相同版本的FSharp.Core? – Brian