我已经编写了我的第一个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)
答案 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列表,那么使用不同的方式编写mergesort。提示:列表不像矢量。它们可能会被这样使用,但通常这样做是错误的。