Стек хвостовой рекурсии F # переполняется, когда не запускается локально - PullRequest
3 голосов
/ 12 июля 2011

У меня проблемы с запуском следующей рекурсивной функции на нашем веб-сервере разработки. Это вызывает переполнение стека. Он работает нормально локально в режиме отладки. Вот что я пробовал:

  1. Убедитесь, что в параметрах сборки включен параметр «Генерировать вызовы».
  2. Я запустил дизассемблер и выполнил следующие инструкции: http://blogs.msdn.com/b/fsharpteam/archive/2011/07/08/tail-calls-in-fsharp.aspx, и он не не выглядит как для использования хвостовой рекурсии.
  3. Я пытался переписать его без использования рекурсии, но мои навыки F # не самые лучшие.

Так что мои вопросы будут:

  1. Эта функция будет в состоянии использовать хвостовые рекурсии?
  2. Почему он работает локально в режиме отладки через VS, а не на веб-сервере dev?

Спасибо!

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 ]

1 голос
/ 12 июля 2011

Поскольку тело вашей функции является выражением последовательности, компилятор не использует хвостовую рекурсию. Однако простой вызов SimulationLoop определенно не должен вызывать переполнение стека, поскольку он должен просто генерировать последовательность без оценки ее содержимого. Кроме того, учитывая природу вашего кода, я ожидал бы, что конечный автомат, который генерирует компилятор для пошагового выполнения последовательности, также не переполняя стек.

Как вы используете результат вызова SimulationLoop, когда видите ошибку? Какие платформы для локальных и веб-машин (например, являются ли они 32-битными)? Если вы урежете свой пример (например, удалив вызовы на CalcPrime, RecordResults и т. Д.), Вы увидите такое же поведение?

0 голосов
/ 14 июля 2011

В итоге я переписал его без рекурсии.Это не самое элегантное решение, но оно работает:

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
...