包(Package)
Table of Contents
1. symbol 的有效范围
Common Lisp 用 package 来限定 symbol 的有效范围,全局变量 *package*
记录了当前所在包(叫作:home-package):
*package* ; => #<PACKAGE "COMMON-LISP-USER">
Common Lisp 启动后默认在 common-lisp-user 包里。
当 REPL 捕获到一个 symbol 时,首先在当前包中查找 symbol 是否存在,否则就创建一个。通过 find-symbol 可以查找 symbol。
相关术语:
- external:在定义 package 时,指定 export 的符号
- internal:未导出的符号
- inherited:defpackage 时,被 :use 的包称为继承包(使用 :use 时,只有外部包可以被导入)
- accessible:1、可访问,只能通过 find-symbol 找到的符号;2、非限定名指向的符号(就是不通过“:”引用的符号)
- present:在包里能找到符号,就是 present
2. 自带的三个标准包
- common-lisp-user,别名是 cl-user。
- common-lisp,别名是 cl。
- keyword,任何包都可以访问 keyword 包里的符号。
3. defpackage
defpackage 用于定义一个新的包,例:
(defpackage :cl-tld (:use :cl) (:export :get-tld :get-domain-suffix :test-cl-tld))
参数说明:
- :import-from,指定符号是从哪个包中导出:
(defpackage #:test1 (:import-from :common-lisp :+)) ; #:test1 这个包里,除了“+”,都必须加包名引用
- :shadow:不继承指定的符号
- :shadowing-import-from:指定符号从哪个包继承
4. use-package
把 accessible 的符号导到当前包里。
5. make-package
一般来说,建议用 defpackage。make-package 和 defpackage 区别如下:
- make-package 可选参数没 defpackage 多,比如没有导出符号的参数,得这样写:
(eval-when (load compile eval) (unless (find-package :just-test) (make-package :just-test :use '(cl)))) (make-package :just-test :use '(cl)) (in-package :just-test) (export 'heihei) ;; 得手动 export 符号 (defun heihei () "heihei")
- make-package 返回包名,可以绑定到符号上:
(defvar just-test (make-package :just-test))
- 需要手动 eval-when,如果写成:
(make-package just-test :use '(cl))
在编译时会报错:
(compile-file "x.lisp") ; => Package JUST-TEST does not exist.
6. 销毁包
使用 unuse-package 可销毁 use-package 导入的所有符号。
(use-package :hi) ; => T ;;; say-hi 是 hi 包里 export 的函数 (say-hi) ; => "hi" (unuse-package :hi) ; => T (say-hi) ; => undefined function: SAY-HI
delete-package 可以销毁加载的包。
7. 与 package 有关的函数
list-all-packages:列出当前加载的所有 package。
package-used-by-list:列出指定的包被哪些包 use 了。