Каковы обычные причины «переполнения стека ошибок C» в реализации Hugs Haskell.Что вызывает «переполнение стека ошибок C» в haskell обычно
ответ
Это может возникнуть, если вы привыкли к функциональным языкам, в которых обычно используется факторизация хвостовой рекурсии. Скажем, у вас есть функция:
sum = go 0
where
go accum [] = accum
go accum (x:xs) = go (accum+x) xs
Который, кстати, такая же, как
sum = foldl (+) 0
Если оценивать функцию мы можем увидеть проблему:
sum [1,2,3,4]
go 0 [1,2,3,4]
go (0+1) [2,3,4]
go ((0+1)+2) [3,4]
go (((0+1)+2)+3) [4]
go ((((0+1)+2)+3)+4) []
(((0+1)+2)+3)+4
Теперь, чтобы оценить, что выражение Haskell использует стопку:
И это, где может произойти переполнение. Если вы оценили сумму [1,10^6], этот стек будет составлять миллион записей.
Но обратите внимание на патологию здесь. Вы переписываете список только для создания огромного выражения fscking («thunk»), а затем используйте стек для его оценки. Мы предпочли бы оценить его, как мы рекурсии, сделав хвостовую рекурсию строгое:
sum = go 0
where
go accum [] = accum
go accum (x:xs) = accum `seq` go (accum+x) xs
Это говорит оценить Accum, прежде чем пытаться оценить рекурсивный вызов, таким образом мы получаем (это может занять некоторое терпение, чтобы понять) :
sum [1,2,3,4]
go 0 [1,2,3,4]
let accum = 0 in accum `seq` go (accum+1) [2,3,4]
go (0+1) [2,3,4]
let accum = 0+1 in accum `seq` go (accum+2) [3,4]
go (1+2) [3,4]
let accum = 1+2 in accum `seq` go (accum+3) [4]
go (3+3) [4]
let accum = 3+3 in accum `seq` go (accum+4) []
go (6+4) []
6+4
10
Итак, как мы обход списка, мы вычисляем сумму, поэтому мы не должны использовать глубокий стек, чтобы оценить результат. Этот модифицированный образец хвостовой рекурсии заключен в Data.List.foldl», так:
sum = foldl' (+) 0
Хорошее эмпирическое правило заключается в никогда не использовать foldl, потому что он всегда строит гигантские санки. Вместо этого используйте foldl '. Если тип вывода имеет ленивую структуру (например, список или функцию), используйте foldr. Но нет серебряной пули, чтобы избежать переполнения стека в целом, вам просто нужно понять поведение вашей программы. Иногда это может быть сложно.
Но, если я правильно понимаю, переполнение стека всегда происходит от попытки оценить гигантский удар. Поэтому ищите места, где они могут быть созданы, и принудительная оценка произойдет раньше.
+1 действительно хорошее объяснение того, как работает складной склад – karoberts
Беглый рекурсия является наиболее вероятным кандидатом ... Вам нужно дать больше информации, хотя для более точного ответа.
Вот код, который может привести к беглым рекурсии:
main =
let x = x :: Int
in print x
Что здесь происходит, что, когда x
оценивается в print x
она начинается, а затем выясняется, что для завершения своей оценки он должен оценить x
.
Это, вероятно, не вызовет переполнения стека из-за TCO (обратите внимание, что x - это хвостовой вызов в x). Это будет бесконечный цикл. – luqui
Хммм ... Так оно и есть, luqui. Я был уверен, что какой-то код вызвал ошибки стека в прошлый раз, когда я попробовал это (правда, несколько лет назад). –
К сожалению. Я был прав, но в другой ситуации. Если я запустил этот код в GHCi, он просто зациклится навсегда. Если я скомпилирую его и запустил, я получаю ошибку: a.out: <
Наиболее вероятной причиной может быть неконтролируемая рекурсия. Каждый рекурсивный вызов потребляет немного больше пространства стека для параметров ввода/вывода.
Только если вы правильно определили «рекурсию». У Haskell есть довольно разные представления о том, что потребляет пространство стека, чем большинство функциональных языков. – luqui
- 1. Что вызывает переполнение стека?
- 2. Haskell - переполнение стека C
- 3. unsafeRead вызывает переполнение стека
- 4. Переполнение стека переполнения Haskell
- 5. Поиск стека вызывает переполнение стека
- 6. Рекурсия вызывает переполнение стека
- 7. compareTo вызывает переполнение стека
- 8. DFS вызывает переполнение стека
- 9. after_initialize вызывает переполнение стека
- 10. рекурсивная функция вызывает переполнение стека
- 11. list :: back() вызывает переполнение стека
- 12. Рекурсивный haskell и переполнение стека
- 13. Как диагностировать переполнение стека в haskell
- 14. Почему мой код C# вызывает переполнение стека
- 15. Перегрузка оператора вызывает переполнение стека
- 16. Emacs tramp вызывает переполнение стека
- 17. инструкция lodsb вызывает переполнение стека
- 18. Переполнение стека в C
- 19. Создание списка вызывает переполнение стека
- 20. Неявное преобразование вызывает переполнение стека
- 21. Конструктор класса вызывает переполнение стека
- 22. Переполнение стека в C
- 23. Что вызывает исключение из первого шанса (переполнение стека) в Qt?
- 24. Что такое переполнение стека?
- 25. Что обычно происходит в C из-за переполнения стека?
- 26. «ERROR - C переполнение стека» в Haskell с помощью Hugs
- 27. Как это не вызывает переполнение стека?
- 28. «ОШИБКА - переполнение стека C» в строках сортировки Haskell
- 29. Переполнение стека рекурсии C++
- 30. Переполнение стека C#
Какая реализация?HUGS? NHC? GHC? –
Все ответы, похоже, говорят о переполнении стека времени выполнения Haskell. Стек C отличается, по крайней мере, в GHC. Это запутанное сообщение об ошибке, или проблема связана с FFI. (Я сделал много работы FFI и никогда не видел этого, но я использую GHC.) – jrockway