Common Lisp包命名与使用问题咨询(含#:符号、删除报错)
作为长期使用Common Lisp的开发者,我来帮你理清这些包相关的困惑,结合你的场景一一解答:
1. #:符号的使用
你提到的#:是Common Lisp里的**未命名符号(uninterned symbol)**读取宏,它生成的符号不会被注册到任何包中。在defpackage里使用它有两个核心作用:
- 避免污染当前包:如果不用
#:,直接写match-test,Lisp会先把这个符号注册(intern)到当前包中,再用它作为新包的名字。而#:match-test不会在当前包留下这个符号,保持当前包的符号表干净。 - 避免命名冲突:假设当前包已经有一个名为
match-test的符号,不用#:的话会直接复用这个符号,而#:生成的是全新的、独立的符号,确保defpackage使用的是你想要的名字。
你的示例代码(defpackage #:match-test (:use #:match #:fiveam #:cl) (:export #:run! #:test-match) (:documentation "..."))是标准且推荐的写法——所有包名和导出符号都用#:,彻底避免当前包的符号干扰。
顺便澄清:#:和函数引用的#'(sharp-quote)是不同的读取宏,前者生成未命名符号,后者是对函数对象的引用,不要混淆哦。
2. 包切换的三种写法为何都生效
(in-package #:match-test)、(in-package :match-test)、(in-package match-test)这三种写法能生效,核心原因是**in-package只关心符号的名字(字符串形式),而不是符号本身属于哪个包**:
#:match-test:未命名符号,名字是"MATCH-TEST",in-package用这个名字查找对应包。:match-test:关键字包(KEYWORD)里的符号,名字同样是"MATCH-TEST",关键字符号的名字总是大写的,所以能匹配到目标包。match-test:当前包中已注册的符号,名字也是"MATCH-TEST",只要当前包存在这个符号,就能找到目标包。
不过推荐使用前两种写法(#:或:前缀):第三种写法依赖当前包必须存在match-test符号,否则会报错;而前两种写法不依赖当前包的符号状态,更可靠。
3. 删除包时的报错原因
你用(delete-package (in-package #:match-test))能删除包但报错,问题出在代码的逻辑顺序和Lisp环境的状态变化:
(in-package #:match-test)会先把当前包切换到match-test。delete-package接着删除这个包——此时当前包被销毁,Lisp环境会自动把当前包切换到一个默认包(比如CL-USER)。delete-package的返回值是布尔值t(表示删除成功),但在打印这个返回值的时候,因为刚发生了当前包的销毁与切换,SBCL的打印机制出现了临时的错误提示(就是你看到的#<BOOLEAN <<error printing object>> {2010004F}>),但实际上删除操作已经完成了。
正确的写法是直接用包名删除,不需要切换到目标包:
(delete-package #:match-test)
这样既完成了删除操作,又不会触发当前包切换带来的打印错误,更简洁可靠。
内容的提问来源于stack exchange,提问作者anquegi




