recursion - Racket中递归的堆栈溢出

标签 recursion lisp racket

作为函数式编程的一部分,递归在 Racket 中得到了高度推广。然而,堆栈溢出是递归中普遍提到的一个重要问题。 Racket 中是否存在任何可能发生堆栈溢出的情况,应该采取哪些预防措施来防止此类事件发生?

最佳答案

没有。你永远不会在 Racket 中遇到堆栈溢出。这是因为 Racket VM 并不真正将内存存储在操作系统级别的调用堆栈中。但是,您可以做的是用完所有机器内存。您可以通过使用需要 Racket VM 不断存储越来越多空间的函数来做到这一点。例如:

(define (f)
  (define x (random))
  (f)
  x)

在此函数中,Racket 在开始返回之前需要存储无限数量的随机 x 值,这将导致您的 VM 耗尽内存。

另一方面,如果您在函数中交换两行:

(define (f)
  (f)
  (define x (random))
  x)

您的函数仍然永远不会终止,并且内存耗尽所需的时间也会长得多。这是因为 VM 只需要记住在完成之前返回到之前对 f 的调用,而不需要为 x 存储空间。

最后,如果我们有这个函数:

(define (f)
  (define x (random))
  x
  (f))

该函数永远不会终止,但它也永远不会耗尽内存。这是因为它为 x 分配了一个空间,但在递归调用 f 时能够删除该空间。此外,由于递归调用是函数执行的最后一件事,它也不再需要存储对 f 的原始调用,这意味着每次递归函数调用都不需要新的空间。这称为尾调用消除。1 实际上,最后一个函数等同于 C 或 Java 中的无限循环。

1请注意,有些人错误地称之为尾调用优化。这不是优化,因为它是语言核心语义的一部分。将其称为“优化”与将 Java 的 GC 称为“优化”一样错误。

关于recursion - Racket中递归的堆栈溢出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39292644/

相关文章:

方案 - 如何替换/更改列表列表中特定位置的元素

Lisp:使用语法糖访问递归哈希

racket - 在某些 Z3 绑定(bind)中是否有不支持声明排序的解决方法?

python - While 循环内的递归函数 (Python)

c++ - 如何使用内联和递归?

C++11 快速 constexpr 整数幂

xml - 在 lisp 程序中删除标签

functional-programming - 用于查找给定函数的列表的最小元素的 Racket 函数

common-lisp - 为什么 Common Lisp 在没有引号的情况下对符号进行评估?

C - 递归搜索函数找到键然后返回NULL