common lisp 中一个常量引起的bug

问题来源于PG的ANSI Common Lisp的第三章的练习6a, 大致意思是, car和cdr功能对调, 那么要你自己写一个cons函数.

当然, car和cdr的功能对调这一点我是忽略的, 我认为问题的重点在于自己实现一个cons函数. 想了一会, 做了一些测试之后, 想到了以下方法:

1 (defun qcons (x y) 
2   (let ((tmp '(nil))) 
3     (setf (car tmp) x
4           (cdr tmp) y)
5     tmp))

这个方法在简单的对atom操作时是没有问题的:

1 [32]> (qcons 'x nil)
2 (X)

但是当嵌套使用时, 程序却出现了卡住的情况, 内存猛涨, cpu占用飙高, 过了一会, repl提示"*** - No more room for LISP objects"(我用的是clisp), 去吃饭时想了一下, 估计是不知什么原因搞成circular list了, 打开*print-circle一看

1 [2]> (setf *print-circle* t)
2 T
3 [3]> (qcons 'x (qcons 'y 'z))
4 #1=(X . #1#)

果然是这样, 开始以为是clisp的bug, 于是我又装了个sbcl来试了一下, 定义函数之后, sbcl提示我说对常量进行了修改之类的操作. 整个函数中只有tmp在初始化的时候赋值了常量, 修改qcons的代码:

1 (defun qcons (x y) 
2   (let ((tmp (list nil))) 
3     (setf (car tmp) x
4           (cdr tmp) y)
5     tmp))

问题解决.

对clisp和sbcl的内部实现不了解(sbcl也出现了这样的情况), 添加代码做了以下实验:

1 (defun qcons (x y) 
2   (let ((tmp '(nil))) 
3     (format t "tmp: ~A~%" tmp)
4     (setf (car tmp) x)
5     (setf (cdr tmp) y)
6     tmp))

执行之前的命令有以下结果:

1 [5]> (qcons 'x (qcons 'y 'z))
2 tmp: (NIL)
3 tmp: (Y . Z)
4 #1=(X . #1#)

第三行输出表明, lisp中的let做了某些"优化", 对每个常量存放在一个固定的地址. 但是由于(qcons 'y 'z)修改了常量'(nil)的值, 并以其作为返回, 导致在第二次调用qcons时, tmp的值没有赋为'(nil), 而是eql于(qcons 'y 'z)的结果, 由此便出现了问题.

具体的东西由于对common lisp的了解还好少, 这里先做个记录, 提醒自己使用destructive函数时要避免常量.

posted on 2012-10-01 21:51  Qwertycen  阅读(369)  评论(0编辑  收藏  举报

导航