list - Common Lisp Mergesort 中的 Stackoverflow

标签 list lisp common-lisp stack-overflow mergesort

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

错误:值堆栈上的堆栈溢出。执行时: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)

最佳答案

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

<强>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、setf 与 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)

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

您可以看到,当我们沿着调用堆栈向下进行时,列表数组没有改变。那是因为 alist 函数参数对于 mergelist 的每次调用都是本地的。

我们需要做的是每次调用 mergelist 时返回一个值 explicity。

(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 循环”,这将帮助您掌握语法以及可以使用循环执行的操作。

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

关于list - Common Lisp Mergesort 中的 Stackoverflow,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37956731/

相关文章:

list - Haskell,à la LYAH中差异列表上的头部和尾部

java - 多态性:为什么使用 "List list = new ArrayList"而不是 "ArrayList list = new ArrayList"?

scheme - 数字的数字列表

lisp - 局部函数中的增量变量 lisp

common-lisp - CFFI-UNIX 发生了什么?

clojure - 带有针对浏览器的阅读器宏的 Lisp?

html - CSS 分布 li 超过 2 行,无限元素

r - 如何创建一个新列,其中包含 df 中的唯一值计数(按 r 中的组)

lisp - CLisp 错误 : "(x) is not number"

lisp - 在common-lisp中转向递归方法