不在本地运行时,F#尾递归堆栈溢出

时间:2011-07-12 14:28:45

标签: f# tail-recursion

我在开发Web服务器上运行以下递归函数时遇到问题。它会导致堆栈溢出。在调试模式下,它在本地运行良好。以下是我尝试过的事情:

  1. 确保在构建选项下启用“生成尾调用”。
  2. 我运行反汇编程序并按照此处的说明进行操作:http://blogs.msdn.com/b/fsharpteam/archive/2011/07/08/tail-calls-in-fsharp.aspx并且它
  3. 我尝试过不使用递归来重写它,但我的F#技能并不是最好的。
  4. 所以我的问题是:

    1. 这个函数是否能够使用尾端递归?
    2. 为什么它会通过VS在调试模式下本地工作,而不是在开发Web服务器上工作?
    3. 谢谢!

      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 -> ()
      }
      

2 个答案:

答案 0 :(得分:1)

因为函数体是序列表达式,所以编译器不使用尾递归。但是,仅调用SimulationLoop绝对不会导致堆栈溢出,因为它应该只生成序列而不评估其内容。此外,鉴于代码的性质,我希望编译器生成的状态机可以逐步运行序列而不会溢出堆栈。

当您看到错误时,如何使用调用SimulationLoop的结果?本地和Web机器的平台是什么(例如它们都是32位)?如果您减少了示例(例如,删除了对CalcPrimeRecordResults等的调用),您会看到相同的行为吗?

答案 1 :(得分: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