Common Lisp:为什么我的尾递归函数会导致堆栈溢出?

2024-01-12

我在理解 Common Lisp 函数的性能方面遇到了问题(我还是个新手)。我有这个函数的两个版本,它只是计算给定的所有整数的总和n.

非尾递归版本:

(defun addup3 (n) 
  (if (= n 0)
    0   
    (+ n (addup (- n 1)))))

尾递归版本:

(defun addup2 (n) 
  (labels ((f (acc k)
              (if (= k 0)
                   acc 
                   (f (+ acc k) (- k 1)))))
  (f 0 n)))

我正在尝试使用输入在 LISP 中运行这些函数n = 1000000。这是结果

[2]> (addup3 1000000)
500000500000
[3]> (addup2 1000000)

*** - Program stack overflow. RESET

我可以在 SBCL 中成功运行两者,但非尾递归更快(仅快一点,但这对我来说似乎很奇怪)。我在 Stackoverflow 上搜索了问题的答案,但找不到类似的东西。尽管尾递归函数设计为不将所有递归函数调用放入堆栈,但为什么会出现堆栈溢出?我是否必须告诉解释器/编译器优化尾部调用? (我读过类似的东西(proclaim '(optimize (debug 1))设置调试级别并以跟踪能力为代价进行优化,但我不知道这是做什么的)。 也许答案是显而易见的,代码是废话,但我就是无法弄清楚。 感谢帮助。

编辑:danlei 指出了拼写错误,应该是调用addup3在第一个函数中,所以它是递归的。如果更正,两个版本都会溢出,但他的版本不会溢出

(defun addup (n) 
         "Adds up the first N integers"
         (do ((i 0 (+ i 1)) 
              (sum 0 (+ sum i)))
             ((> i n) sum)))

虽然这可能是一种更典型的方法,但我觉得奇怪的是,尾递归并不总是优化的,考虑到我的导师喜欢告诉我它效率更高。


Common Lisp 实现不需要进行尾调用优化。然而,大多数都这样做(我认为 ABCL 不这样做,因为 Java 虚拟机的限制)。

实施文档应告诉您应选择哪些优化设置来实现 TCO(如果可用)。

对于 Common Lisp 代码来说,使用其中一种循环结构更为惯用:

(loop :for i :upto n
      :sum i)

(let ((sum 0))
  (dotimes (i n)
    (incf sum (1+ i))))

(do ((i 0 (1+ i))
     (sum 0 (+ sum i)))
    ((> i n) sum))

当然,在这种情况下,最好使用“小 Gauß”:

(/ (* n (1+ n)) 2)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Common Lisp:为什么我的尾递归函数会导致堆栈溢出? 的相关文章

  • 使 clos 对象可在 lisp 中打印

    如果你想让 common lisp 中的 CLOS 对象可打印 可读打印 你如何在不使用除了 print 和 read 之外的任何东西的情况下做到这一点 至少在我的解决方案中 有两个部分可以做到这一点 但是您将需要这个功能 感谢 cl pr
  • common lisp:宏如何使用以编程方式生成的名称定义其他方法/宏?

    我意识到我的代码的某个部分由看起来相似的方法组组成 就像我有多个三重奏 一个由程序员的其他两个函数调用的辅助函数 我正在尝试编写一个宏来为我定义这三个函数 以便我所需要做的就是调用该宏 但我的尝试导致 defun 和函数调用将引用字符串而不
  • 如何使用 common lisp 确定操作系统和主机名?

    为了让我的 sbclrc 文件在我使用的两台计算机上工作 我想要一种从 sbcl 中获取主机名和 或操作系统的方法 我知道我可以设置然后查找环境变量 但是有更直接的方法吗 Update 我将问题更改为引用 common lisp 因为 Ke
  • 对 SBCL 中的“ql:quickload”和可执行脚本感到困惑

    我一直在尝试在我的可执行脚本中使用 Quicklisp 包 一个 简单的 工作示例是 usr bin sbcl script eval when compile toplevel load toplevel execute ql quick
  • 如何在 SLIME 的 REPL 中获得 Common Lisp 的语法高亮显示?

    我想学习 Common Lisp 并通过 emacs 包管理器安装了 emacs 24 3 和 slime 在 slime REPL 语法高亮中不起作用 另一方面 当我启动 Lisp Mode 在 slime REPL 中 时 表达式的值不
  • 在lisp中,如何使用floor函数返回的第二个值?

    当我这样做时 4楼3 我得到了 1 1 3 但我该如何使用这 1 3 呢 例如 您可以使用将其绑定到变量multiple value bind multiple value bind quot rem floor 4 3 format t
  • 在 LISP 中实现基本库函数(手动)

    有什么方法可以定义函数my list my cons my append其执行类似的功能list cons and append分别 否则哪里可以找到这些功能的实现呢 Thanks 对于my list和my append 解决方案是 def
  • 通过 Emacs 启动时如何配置 SBCL 以使用更多 RAM?

    如何配置 SBCL 使其在使用 Emacs 中的 M x slime 启动时使用比默认值更多的内存 从我在网上看到的情况来看 答案似乎是调用 SBCL 传递参数 dynamic space size 由于我不直接调用 SBCL 因此我不知道
  • CLISP - 反转简单列表

    我必须反转简单 单维 列表的元素 我知道有一个内置的反向函数 但我不能用它来做这个 这是我的尝试 defun LISTREVERSE LISTR cond lt length LISTR 2 LISTR listr is 1 atom or
  • Common Lisp 中的(随机)不那么随机?

    好的 最后一个问题 我将用 Common Lisp 完成我的猜数游戏 D 每当游戏开始 或者在第一个游戏之后开始新游戏 时 都会调用以下函数 Play the game defun play If it s their first time
  • 在 Parenscript 中使用 regex(正则表达式)

    我正在尝试 Parenscript 在尝试使用正则表达式函数时 我得到了意外的输出 例如 参考手册 https common lisp net project parenscript reference html shows regex f
  • 我应该在 Common Lisp 中使用哪些正则表达式库? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 宏扩展可以包含(声明...)表达式吗?

    Common Lisp Hyperspec 规定 宏形式不能扩展为声明 声明表达式必须显示为它们引用的形式的实际子表达式 我对 扩展到 的含义感到困惑 由于显而易见的原因 如下宏将不起作用 defmacro optimize fully d
  • 使用包阴影符号

    例如 我有这个包定义 它遮蔽了 COMMON LISP LISTEN defpackage shadows use common lisp shadow listen export listen 然后我想使用另一个包中的这个包 比如说 de
  • 阿克曼函数的这种实现可以称为尾递归吗?

    我用 C 语言编写了以下代码 我们可以将其称为尾递归实现吗 include
  • 为什么 .NET/C# 不优化尾调用递归?

    I found 这个问题 https stackoverflow com questions 340762 which languages support tail recursion optimization关于哪些语言优化尾递归 为什么
  • 宏、Clojure 与 Common Lisp

    我和我的一些朋友正在开发一个新平台 我们想用 lisp 构建它 主要吸引力是宏 我们都使用 Common Lisp 但我想探索 Clojure 的选择 当我提出这一点时 其中一位说宏观体系 较弱 我想知道这是否属实 以及在哪些领域 就您可以
  • 方案中的尾递归幂函数

    我在方案中编写尾递归幂函数时遇到问题 我想使用辅助函数来编写该函数 我知道我需要一个参数来保存累计值 但在那之后我就陷入了困境 我的代码如下 define pow tr a b define pow tr h result if b 0 r
  • 如何在Scala中实现尾递归快速排序

    我写了一个递归版本 def quickSort T xs List T p T T gt Boolean List T xs match case Nil gt Nil case gt val x xs head val left righ
  • 为什么在 emacs-lisp 中的函数参数之前使用#'?

    我熟悉 Emacs Lisp 但不熟悉 Common 或任何其他 Lisp 一些 Lisp 程序员建议 例如emacs 的基本功能 https stackoverflow com questions 17076646 a basic fun

随机推荐