Common Lisp Mergesort中的Stackoverflow

时间:2016-06-22 00:31:54

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

我已经编写了我的第一个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)

2 个答案:

答案 0 :(得分:3)

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

<强> 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&#39; s,setf&#39; s vs Let。

上面的代码通过setq对它们在顶级环境中创建变量(当然,除非你在其他地方对它们进行了defparametered)。当程序变大时,这可能会产生一些不良副作用(如果两个函数同时使用&#34; i&#34;同时?)。最好使用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:(MERGESORT(5 1 4 2 3)0 4)     1:(MERGESORT(5 1 4 2 3)0 2)       2:(MERGESORT(5 1 4 2 3)0 1)         3:(MERGESORT(5 1 4 2 3)0 1/2)           4:(MERGESORT(5 1 4 2 3)0 1/4)             5:(MERGESORT(5 1 4 2 3)0 1/8)               6:(MERGESORT(5 1 4 2 3)0 1/16)                 7:(MERGESORT(5 1 4 2 3)0 1/32)                   8:(MERGESORT(5 1 4 2 3)0 1/64)                     9:(MERGESORT(5 1 4 2 3)0 1/128)                       10:(MERGESORT(5 1 4 2 3)0 1/256)                         11:(MERGESORT(5 1 4 2 3)0 1/512)                           12:(MERGESORT(5 1 4 2 3)0 1/1024)                             13:(MERGESORT(5 1 4 2 3)0 1/2048)                               14:(MERGESORT(5 1 4 2 3)0 1/4096)                                 15:(MERGESORT(5 1 4 2 3)0 1/8192)                                   16:(MERGESORT(5 1 4 2 3)0 1/16384)

在第600行,你看

600 :( MERGESORT(5 1 4 2 3)0                                         1/1037378892220248239628101965922790287753111558060609224998914332422663202853227036599926762236775948572049471652825197295598787768852943826971718708528490921765295450850377380921344)

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

您可以看到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*
  ))

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

此外,您必须在mergesort中捕获该返回值,并且mergesort也会返回显式值。

我还建议您阅读一些关于循环宏的内容 - 谷歌&#34;适用于黑带的实用Common Lisp循环&#34;这将帮助您掌握语法以及您可以使用循环执行的操作。

现在,代码中仍有一些问题需要解决,但我希望我已经给你足够的时间来完成这个迭代

答案 1 :(得分:3)

代码有太多错误:

  • 将列表视为向量:如果您希望Lisp数据结构具有随机访问权限,则使用向量。不是清单。
  • 不会声明变量
  • while循环根本不起作用
  • 命令式,而不是功能
  • 错误的代码布局

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