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:
- Made sure 'Generate tail calls' is enabled under build options.
- 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.
- I've tried rewriting it without using recursion but my F# skills are not the best.
So my questions would be:
- Is this function going to be able to use tail end recursions?
- 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
精彩评论