开发者

F# tail recursion stack overflows when not run locally

开发者 https://www.devze.com 2023-03-19 23:07 出处:网络
I am having problems running the below recursive function on our development web server. It causes a stack overflow. It runs fine locally when in debugging mode. Here are things I have tried:

I am having problems running the below recursive function on our development web server. It causes a stack overflow. It runs fine locally when in debugging mode. Here are things I have tried:

  1. Made sure 'Generate tail calls' is enabled under build options.
  2. I ran the disassembler and followed the instructions here: http://blogs.msdn.com/b/fsharpteam/archive/2011/07/08/tail-calls-in-fsharp.aspx and it does not appear to be using tail recursion.
  3. I've tried rewriting it without using recursion but my F# skills are not the best.

So my questions would be:

  1. Is this function going to be able to use tail end recursions?
  2. Why would it work locally in debugging mode through VS but not on the dev web server?

Thanks!

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


Because the body of your function is a sequence expression, the compiler doesn't use tail recursion. However, merely calling SimulationLoop should definitely not cause a stack overflow since it should just generate the sequence without evaluating its contents. Furthermore, given the nature of your code, I would expect the state machine that the compiler generates for stepping through the sequence to run without overflowing the stack as well.

How are you using the result of calling SimulationLoop when you see the error? What are the platforms for the local and web machines (e.g. are they both 32-bit)? If you trim down your example (e.g. by removing the calls to CalcPrime, RecordResults, etc.) do you see the same behavior?


I ended up rewriting it without recursion. This is not the most elegant solution but it works:

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
0

精彩评论

暂无评论...
验证码 换一张
取 消