【问题标题】:Stackoverflow in Common Lisp MergesortCommon Lisp 合并排序中的 Stackoverflow
【发布时间】:2023-03-23 19:58:01
【问题描述】:

我已经编写了我的第一个 Common Lisp 函数,但我无法追踪我的错误是在哪里产生的。如何解决以下错误:

Error: Stack overflow on value stack. While executing: TRUNCATE

这是我的代码:

(defun mergelist (alist low mid high)
    (setq i1 low)
    (setq i2 (+ mid 1))
    (setq i low)
    (setq blist `())
    (loop while (and (<= i1 mid) (<= i2 high)) do
        (if (<= (nth i1 alist) (nth i2 alist))
            (setf (nth (+ i 1) blist) (nth (+ i1 1) alist))
            (setf (nth (+ i 1) blist) (nth (+ i2 1) alist))
        )
    )
    (loop while (<= i1 mid) do
       (setf (nth (+ i 1) blist) (nth (+ i1 1) alist)) 
    )
    (loop while (<= i2 high) do
        (setf (nth (+ i 1) blist) (nth (+ i2 1) alist))  
    )
    (setq j low)
    (loop for j from j to high do
        (setf (nth i alist) (nth i blist))
    )
)

(defun mergesort (alist low high)
    (when (< low high)
        (mergesort alist low (/ (+ low high) 2))
        (mergesort alist (/ (+ low high) (+ 2 1)) high)
        (mergelist alist low (/ (+ low high) 2) high)
    )
)

以下是我测试函数的方式:

(setq dlist `(5 1 4 2 3))
(mergesort dlist 0 4)

我的预期回报是:

(1 2 3 4 5)

【问题讨论】:

  • 我建议改为阅读 Lisp 入门书籍。请参阅:cs.cmu.edu/~dst/LispBook/index.html 您的代码不可修复。是富吧。在 Lisp 中,几乎你代码中的每一行都是错误的。它看起来像 C,但在 Lisp 中不起作用。扔掉它。白手起家。尝试编写 Lisp 代码,而不是 C 代码。
  • 您的 mergelist 已由 Common Lisp 提供为 merge

标签: list lisp common-lisp stack-overflow mergesort


【解决方案1】:

我们可以做很多事情来改进这段代码。

1.缩进

Lisp 的语法相对较少,但我们使用缩进来帮助突出代码的结构。大多数支持 Lisp 的编辑器都会帮助管理它。与传统缩进方法最明显的不同是以下行的右括号。我缩进了 mergelist 函数以显示更易读的函数体 - 至少对我来说是这样。

(defun mergelist (alist low mid high) 
  (setq i1 low)
  (setq i2 (+ mid 1))
  (setq i low)
  (setq blist `())
  (loop while (and (<= i1 mid) (<= i2 high)) do
       (if (<= (nth i1 alist) (nth i2 alist))
       (setf (nth (+ i 1) blist) (nth (+ i1 1) alist))
       (setf (nth (+ i 1) blist) (nth (+ i2 1) alist))))
  (loop while (<= i1 mid) do
       (setf (nth (+ i 1) blist) (nth (+ i1 1) alist)))
  (loop while (<= i2 high) do
       (setf (nth (+ i 1) blist) (nth (+ i2 1) alist)))
  (setq j low)
  (loop for j from j to high do
       (setf (nth i alist) (nth i blist))))

2。 Setq's, setf's vs Let.

上面的代码通过设置变量在顶级环境中创建变量(当然,除非您在其他地方对它们进行了defparametered)。随着程序变大,这可能会产生一些不良副作用(如果两个函数同时使用“i”会怎样?)。最好使用 LET 创建局部词法变量,例如

(defun mergelist-2 (alist low mid high)
  (let ((i1 low)
    (i2 (+ mid 1)
      i (low)
      blist '()))

   (loop while (and (<= i1 mid) (<= i2 high)) do
       (if (<= (nth i1 alist) (nth i2 alist))
       (setf (nth (+ i 1) blist) (nth (+ i1 1) alist))
       (setf (nth (+ i 1) blist) (nth (+ i2 1) alist))))
  (loop while (<= i1 mid) do
       (setf (nth (+ i 1) blist) (nth (+ i1 1) alist)))
  (loop while (<= i2 high) do
       (setf (nth (+ i 1) blist) (nth (+ i2 1) alist)))
  (setq j low)
  (loop for j from j to high do
       (setf (nth i alist) (nth i blist))) ))

3。表单可以返回值

lisp 形式通常会返回一个值。如果我们在 repl 中输入 (+ 1 2),我们将看到 3。defun 通常会返回一个值,通常作为其主体中的最后一个形式。

如果我们查看 mergelist,我们会发现它不是显式返回任何值,而是尝试使用变量 alist 来传达返回值。这不起作用!

Lisp 提供了跟踪工具,让我们了解里面发生了什么

这是前 16 行跟踪。我的系统在第 600 行崩溃了

0: (合并 (5 1 4 2 3) 0 4) 1:(合并(5 1 4 2 3)0 2) 2:(合并(5 1 4 2 3)0 1) 3:(合并(5 1 4 2 3)0 1/2) 4:(合并(5 1 4 2 3)0 1/4) 5:(合并(5 1 4 2 3)0 1/8) 6:(合并(5 1 4 2 3)0 1/16) 7:(合并(5 1 4 2 3)0 1/32) 8:(合并(5 1 4 2 3)0 1/64) 9:(合并(5 1 4 2 3)0 1/128) 10:(合并(5 1 4 2 3)0 1/256) 11:(合并(5 1 4 2 3)0 1/512) 12:(合并(5 1 4 2 3)0 1/1024) 13: (合并 (5 1 4 2 3) 0 1/2048) 14:(合并(5 1 4 2 3)0 1/4096) 15:(合并(5 1 4 2 3)0 1/8192) 16:(合并(5 1 4 2 3)0 1/16384)

在第 600 行看到

600: (合并 (5 1 4 2 3) 0 1/1037378892220248239628101965922790287753111558060609224998914332422663202853227036599926762236775948572049471652825197295598787768852943826971718708528490921765295450850377380921344) P>

这是一个非常小的数字,它解释了有关截断的错误消息。

您可以看到 alist 数组在我们继续调用堆栈时没有改变。那是因为 alist 函数参数是每次调用 mergelist 的本地参数。

我们需要做的是让mergelist在每次调用时都返回一个显式值。

(defun mergelist-3 (alist low mid high)
  (let ((i1 low)
    (i2 (+ mid 1)
      i (low)
      j
      blist '()))

   (loop while (and (<= i1 mid) (<= i2 high)) do
       (if (<= (nth i1 alist) (nth i2 alist))
       (setf (nth (+ i 1) blist) (nth (+ i1 1) alist))
       (setf (nth (+ i 1) blist) (nth (+ i2 1) alist))))
  (loop while (<= i1 mid) do
       (setf (nth (+ i 1) blist) (nth (+ i1 1) alist)))
  (loop while (<= i2 high) do
       (setf (nth (+ i 1) blist) (nth (+ i2 1) alist)))
  (setq j low)
  (loop for j from j to high do
       (setf (nth i alist) (nth i blist)))
  *;return value here*
  ))

作为进一步的提示,函数中的最后一个循环是不需要的。

此外,您必须在合并排序中捕获该返回值,并让合并排序也返回一个显式值。

我还建议您阅读一些有关循环宏的信息 - 谷歌搜索“黑带实用通用 Lisp 循环”,这将帮助您掌握循环的语法和可以做的事情。

现在,代码中仍有一些问题需要修复,但我希望我已经给了你足够的信息来完成这次迭代

【讨论】:

  • 谢谢,这很有帮助。这是我的一次尝试。怀疑我会再次使用 Common Lisp。只是试图获得足够的基础知识来把这个简单的排序放在一起。找不到 Common Lisp 的资源。
  • @defaultNINJA 还值得指出的是,您的 mergelist 已经由 Common Lisp 作为merge 提供。
  • @defaultNINJA 如需更多资源,请查看:stackoverflow.com/questions/7224823/…
  • @defaultNINJA,我希望你至少继续这个迷你项目。到目前为止,你几乎没有接触过普通的口齿不清。下一步是清理循环。如果遇到困难,请随时发布后续问题。
【解决方案2】:

代码错误太多:

  • 将列表视为向量:如果您想要具有随机访问的 Lisp 数据结构,请使用向量。不是列表。
  • 不声明变量
  • while 循环根本不起作用
  • 命令式,而不是函数式
  • 错误的代码布局

最好从头开始,避免上述错误。如果您想要 Lisp 列表,请使用不同的方式来编写合并排序。提示:列表不像向量。它们可能会被这样使用,但通常这样做是错误的。

【讨论】:

  • 不错的尝试... Common Lisp 允许这样做。阅读 Common Lisp。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-02-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-08
相关资源
最近更新 更多