Я не могу точно определить, почему я получаю *** Исключение: переполнение стека

#haskell #haskell-stack

#haskell #haskell-stack

Вопрос:

Я пишу функции, которые вызывают другие функции. Все работало нормально, пока я не получил комментарий -A6 AGP (t). Когда я пытаюсь запустить agp1, он выдает *** Исключение: переполнение стека.

Я пытался добавлять и удалять круглые скобки, чтобы ни одна функция не вызывалась снова и снова, но по-прежнему безуспешно.

 import Data.List
import System.IO

p = 1.50
dt= 0.050
at = 0.075
wMax = 20.00
da1 = 300.0
dcasa = 0.05
dSort = 1.50
dKafv = 0.50
aa1 = 250.0
acasa = 0.05
aKafv = 0.25
aSort = 1.00
v = 1200.0
l = 47490.0


--A1 AGL(t)
agl :: Float -> Float
agl a = a   5

--A6 AGP(t)
agp :: Float -> Float
agp x = (agp (x-1) - ((at - agp (x-1)) / at)) * (atl (x-1) - at)

agp1 =  agp 2.0

--A7 ATL(t)
atl :: Float -> Float
atl x = (agl x - agl (x 1)) / agl (x)

--A10 DCAS(t)
dcas :: Float -> Float
dcas x = (l/v)*da1*((1.0-dcasa)**(dSort*(x - 1)))*dKafv*(((1 - ((1- 
         dcasa)**(dSort 1))) / dcasa) - 1)


--A11 ACAS(t)
acas :: Float -> Float
acas x = (l/v)*aa1*((1.0-acasa)**(aSort*(x - 1)))*aKafv*(((1 - ((1- 
          acasa)**(aSort 1))) / acasa) - 1)

--A12 Da(t)
da :: Float -> Float
da x = da1 * ((1.0 - dcasa) ** (dSort * (x - 1)))


--A13 Aa(t)
aa :: Float -> Float
aa x = aa1 * ((1.0 - acasa) ** (x - 1))
  

Комментарии:

1. agp это рекурсивная функция, которая всегда выполняет рекурсивный вызов — у нее нет «базового варианта», на котором она останавливается, поэтому она никогда не перестанет вычислять. (Обратите внимание, что «переполнение стека» в Haskell происходит не из-за того, что он заполняется вызовами функций, как это было бы на других языках — это из-за «thunks», то есть недооцененных выражений. Но основная причина здесь та же, у вас бесконечная рекурсия.)

2. agp x = forever (consume stack space) Да, это плохо.

3. Обратите внимание, что код, который вы на самом деле показываете, не вызывает проблемы, потому что ничто не влияет на значение agp1 . Попытка напечатать его значение принудительно приведет к бесконечной рекурсии. (Я предполагаю, что вы пропустили какой-то код, учитывая разрыв между A1 и A6 и тот факт, что существует несколько неопределенных функций, например agl .)

4. @RobinZigmond Если я сделаю базовый вариант: agp 0.0 = 0.0, я все равно получу бесконечную рекурсию. Я неправильно использую базовый вариант?

5. @AprilWilliams Базовый вариант может быть недоступен, если, скажем, начать с agp 100.5 поскольку мы переходим от 0.5 к -0.5 . Кроме того, ошибки округления могут вызвать проблему, поскольку 0.000000001 не учитывается 0.0 , поэтому базовый вариант не соблюдается. Вы могли бы попытаться определить agp x | x <= 0.001 = 0 так, чтобы значения, достаточно близкие к 0, и отрицательные значения действовали как базовый вариант. Или вы могли бы использовать Int в качестве аргумента (а затем преобразовать его в float внутри функции, если это необходимо).

Ответ №1:

Вам нужен какой-то базовый вариант, в котором agp не вызывается само по себе, чтобы вычисление закончилось. Я предполагаю, что ваша функция должна быть такой:

 --A6 AGP(t)
agp :: Float -> Float
agp x
    | agp' > 0 = agp'
    | otherwise = 0
    where agp' = (agp (x-1) - ((at - agp (x-1)) / at)) * (atl (x-1) - at)