我们可以做很多事情来改进这段代码。
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 循环”,这将帮助您掌握循环的语法和可以做的事情。
现在,代码中仍有一些问题需要修复,但我希望我已经给了你足够的信息来完成这次迭代