Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 为什么应用于两个参数都严格的函数的foldr不会导致堆栈溢出?_Haskell_Recursion_Stack Overflow_Fold - Fatal编程技术网

Haskell 为什么应用于两个参数都严格的函数的foldr不会导致堆栈溢出?

Haskell 为什么应用于两个参数都严格的函数的foldr不会导致堆栈溢出?,haskell,recursion,stack-overflow,fold,Haskell,Recursion,Stack Overflow,Fold,我想知道为什么这个表达式不会导致GHCi中的堆栈溢出: foldr (+) 0 [1..5000000] -- 12500002500000 显然,(+)的两个参数都很严格,因此必须立即遍历整个列表,懒散也无济于事 我的第一个想法是,编译器会将(+)识别为关联操作,并将其转换为尾部递归 但是,以下操作也可以工作: foldr (-) 0 [1..5000000] -- -2500000 这里发生了什么?GHC最新的运行时系统允许堆栈动态增长。尝试限制它,您将看到堆栈溢出: % ghci +R

我想知道为什么这个表达式不会导致GHCi中的堆栈溢出:

foldr (+) 0 [1..5000000] -- 12500002500000
显然,
(+)
的两个参数都很严格,因此必须立即遍历整个列表,懒散也无济于事

我的第一个想法是,编译器会将
(+)
识别为关联操作,并将其转换为尾部递归

但是,以下操作也可以工作:

foldr (-) 0 [1..5000000] -- -2500000

这里发生了什么?

GHC最新的运行时系统允许堆栈动态增长。尝试限制它,您将看到堆栈溢出:

% ghci +RTS -K512K
GHCi, version 8.2.2: http://www.haskell.org/ghc/  :? for help
Prelude> foldr (+) 0 [1..5000000]
*** Exception: stack overflow

实际上,它确实会导致堆栈溢出——只是不是在固定大小的堆栈上,而是在可按需增长的堆上。所以它不会使你的程序崩溃,而是让你的操作系统。。。不仅仅是5000000个元素,但是如果你尝试更多,请准备好一些交换乐趣。我希望它不会使现代操作系统崩溃。@chepner不会崩溃,但当内核开始从窗口管理器中交换时,它可以使它几乎停止,为流氓Haskell程序腾出空间。建议使用
-使用rtsopts'-M4g'
或类似工具进行编译。为什么首先限制堆栈大小?这对于命令式语言很有意义,因为循环是惯用的,递归不是。@ftor我不确定,但我怀疑这是一种32位的计算遗产。当你只有4个Gig可玩时,内存管理实际上是一个需要注意的问题;一种流行的策略是选择一个固定大小的堆栈,将其放在地址空间中的某个特定位置,然后将剩余的地址空间用于堆。一旦移动到64位地址,问题就小得多了,因为很容易将堆栈和堆放在地址空间的不同部分——技术上仍然有限制,但没有一个RAM能够长时间匹配。我不确定内存管理是否仍然是这样工作的,但我记得90年代在经典Mac OS上处理堆栈-堆冲突。堆栈从地址空间0开始并逐渐增大;堆开始于,然后逐渐变小。在其中一个上分配太多内存,其中一个将开始覆盖另一个。(当然,每个进程都分配了一个固定大小的全局地址空间块,而不是一个完整的32位虚拟地址空间,因此使用可用内存要容易得多。)